commit 1c0dd06b6ed4df6fcba6e5aa830f733f6bd89af2 Author: ebner Date: Fri Oct 21 09:04:00 2011 +0200 Migrated from Mercurial to Git (without keeping history) diff --git a/ch.psi.fda/.classpath b/ch.psi.fda/.classpath new file mode 100644 index 0000000..961bd3e --- /dev/null +++ b/ch.psi.fda/.classpath @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/.gitignore b/ch.psi.fda/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/ch.psi.fda/.gitignore @@ -0,0 +1 @@ +/target diff --git a/ch.psi.fda/.project b/ch.psi.fda/.project new file mode 100644 index 0000000..e064849 --- /dev/null +++ b/ch.psi.fda/.project @@ -0,0 +1,23 @@ + + + ch.psi.fda + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + org.eclipse.jdt.core.javanature + + diff --git a/ch.psi.fda/.settings/org.eclipse.jdt.core.prefs b/ch.psi.fda/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..74b8c7b --- /dev/null +++ b/ch.psi.fda/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,3 @@ +#Wed Oct 19 12:49:33 CEST 2011 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning diff --git a/ch.psi.fda/.settings/org.eclipse.m2e.core.prefs b/ch.psi.fda/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..b2af8ce --- /dev/null +++ b/ch.psi.fda/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,5 @@ +#Tue Oct 18 15:52:42 CEST 2011 +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/ch.psi.fda/pom.xml b/ch.psi.fda/pom.xml new file mode 100644 index 0000000..6370703 --- /dev/null +++ b/ch.psi.fda/pom.xml @@ -0,0 +1,159 @@ + + 4.0.0 + ch.psi + fda + 1.1.27 + + + + ch.psi + jcae + 1.0.28 + + + + ch.psi + plot + 1.1.30 + + + + org.samba + jcifs + 1.3.14 + + + + + + org.jython + jython + 2.5.1 + + + + org.apache.commons + cli + 1.2 + + + + com.jmatio + jmatio + 0.2u2psi1 + + + + javax.mail + mail + 1.4.1 + + + + org.freehep.io + xdr + 2.0.4 + + + + junit + junit + 4.8.2 + test + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.8 + + + attach-javadocs + + jar + + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.1.2 + + + attach-sources + + jar + + + + + + + org.jvnet.jaxb2.maven2 + maven-jaxb2-plugin + 0.8.0 + + + + generate + + + + + ch.psi.fda.model.v1 + + + + + org.codehaus.mojo + xml-maven-plugin + 1.0 + + + transform + + transform + + + + + + + + src/main/resources + src/main/documentation/xs3p.xsl + + + \.xsd$ + .html + + + + + + + + + + + + i.snapshots + Artifactory Snapshots + http://slsyoke1/artifactory/libs-snapshots-local + + + i.releases + Atrifactory Releases + http://slsyoke1/artifactory/libs-releases-local + + + \ No newline at end of file diff --git a/ch.psi.fda/site/pictures/fda_small.svg b/ch.psi.fda/site/pictures/fda_small.svg new file mode 100644 index 0000000..92d7e18 --- /dev/null +++ b/ch.psi.fda/site/pictures/fda_small.svg @@ -0,0 +1,237 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + fda + 2.0 + + diff --git a/ch.psi.fda/site/pictures/fda_splashScreen.png b/ch.psi.fda/site/pictures/fda_splashScreen.png new file mode 100644 index 0000000..3c912eb Binary files /dev/null and b/ch.psi.fda/site/pictures/fda_splashScreen.png differ diff --git a/ch.psi.fda/site/pictures/fda_splashScreen.svg b/ch.psi.fda/site/pictures/fda_splashScreen.svg new file mode 100644 index 0000000..9dd4576 --- /dev/null +++ b/ch.psi.fda/site/pictures/fda_splashScreen.svg @@ -0,0 +1,1153 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fda + + + diff --git a/ch.psi.fda/site/pictures/fda_transparent.png b/ch.psi.fda/site/pictures/fda_transparent.png new file mode 100644 index 0000000..047528e Binary files /dev/null and b/ch.psi.fda/site/pictures/fda_transparent.png differ diff --git a/ch.psi.fda/site/pictures/logo_only.png b/ch.psi.fda/site/pictures/logo_only.png new file mode 100644 index 0000000..a768370 Binary files /dev/null and b/ch.psi.fda/site/pictures/logo_only.png differ diff --git a/ch.psi.fda/src/main/documentation/xs3p.xsl b/ch.psi.fda/src/main/documentation/xs3p.xsl new file mode 100644 index 0000000..b127948 --- /dev/null +++ b/ch.psi.fda/src/main/documentation/xs3p.xsl @@ -0,0 +1,8503 @@ + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + false + + + false + + + + + + + + + + + + + + + http://www.w3.org/2001/XMLSchema + + + http://www.w3.org/XML/1998/namespace + + + 1.5 + + + 0.5 + + + XML Schema Documentation + + + + type_ + + attribute_ + + attributeGroup_ + + + + element_ + + key_ + + group_ + + notation_ + + ns_ + + + + term_ + + + + + + + + + + + + true + +'linksFile' variable must be provided if either +'searchIncludedSchemas' or 'searchImportedSchemas' is true. + + + + + + + + + + + + + + + + + + + + <xsl:value-of select="$actualTitle"/> + + + + + + + + + + + + + + + + + + + + + + + + + +

+ + + + + +

Table of Contents

+ + + + +

Schema Document Properties

+ + + +

Declared Namespaces

+ + + + + + + + + +

Redefined Schema Components

+ +
+ + + + + + + +

Global Declarations

+ + + + +
+ + +

Global Definitions

+ + + + +
+
+ + +

Global Schema Components

+ +
+
+ + + +
+

Legend

+ + +
+
+ + + +
+

Glossary

+ + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
PrefixNamespace
+ Default namespace + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
  • + + + + + : + + +
  • +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +var pc = getElementObject("printerControls"); +if (pc != null) { + pc.style.display="block"; +} + + + + + + + +var gc = getElementObject("globalControls"); +if (gc != null) { + gc.style.display="block"; +} + + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + +

    + + + + + + + + + + + + + : + + + + +

    +
    + + + + + +
    +
    + + + + + /* IDs of XML Instance Representation boxes */ + + var xiBoxes = new Array( + + + , + + ' + + + + _xibox' + + ); + + + /* IDs of Schema Component Representation boxes */ + + var scBoxes = new Array('schema_scbox' + + , ' + + + + _scbox' + + ); + + + +/** + * Can get the ID of the button controlling + * a collapseable box by concatenating + * this string onto the ID of the box itself. + */ +var B_SFIX = "_button"; + +/** + * Counter of documentation windows + * Used to give each window a unique name + */ +var windowCount = 0; + +/** + * Returns an element in the current HTML document. + * + * @param elementID Identifier of HTML element + * @return HTML element object + */ +function getElementObject(elementID) { + var elemObj = null; + if (document.getElementById) { + elemObj = document.getElementById(elementID); + } + return elemObj; +} + +/** + * Closes a collapseable box. + * + * @param boxObj Collapseable box + * @param buttonObj Button controlling box + */ +function closeBox(boxObj, buttonObj) { + if (boxObj == null || buttonObj == null) { + // Box or button not found + } else { + // Change 'display' CSS property of box + boxObj.style.display="none"; + + // Change text of button + if (boxObj.style.display=="none") { + buttonObj.value=" + "; + } + } +} + +/** + * Opens a collapseable box. + * + * @param boxObj Collapseable box + * @param buttonObj Button controlling box + */ +function openBox(boxObj, buttonObj) { + if (boxObj == null || buttonObj == null) { + // Box or button not found + } else { + // Change 'display' CSS property of box + boxObj.style.display="block"; + + // Change text of button + if (boxObj.style.display=="block") { + buttonObj.value=" - "; + } + } +} + +/** + * Sets the state of a collapseable box. + * + * @param boxID Identifier of box + * @param open If true, box is "opened", + * Otherwise, box is "closed". + */ +function setState(boxID, open) { + var boxObj = getElementObject(boxID); + var buttonObj = getElementObject(boxID+B_SFIX); + if (boxObj == null || buttonObj == null) { + // Box or button not found + } else if (open) { + openBox(boxObj, buttonObj); + // Make button visible + buttonObj.style.display="inline"; + } else { + closeBox(boxObj, buttonObj); + // Make button visible + buttonObj.style.display="inline"; + } +} + +/** + * Switches the state of a collapseable box, e.g. + * if it's opened, it'll be closed, and vice versa. + * + * @param boxID Identifier of box + */ +function switchState(boxID) { + var boxObj = getElementObject(boxID); + var buttonObj = getElementObject(boxID+B_SFIX); + if (boxObj == null || buttonObj == null) { + // Box or button not found + } else if (boxObj.style.display=="none") { + // Box is closed, so open it + openBox(boxObj, buttonObj); + } else if (boxObj.style.display=="block") { + // Box is opened, so close it + closeBox(boxObj, buttonObj); + } +} + +/** + * Closes all boxes in a given list. + * + * @param boxList Array of box IDs + */ +function collapseAll(boxList) { + var idx; + for (idx = 0; idx < boxList.length; idx++) { + var boxObj = getElementObject(boxList[idx]); + var buttonObj = getElementObject(boxList[idx]+B_SFIX); + closeBox(boxObj, buttonObj); + } +} + +/** + * Open all boxes in a given list. + * + * @param boxList Array of box IDs + */ +function expandAll(boxList) { + var idx; + for (idx = 0; idx < boxList.length; idx++) { + var boxObj = getElementObject(boxList[idx]); + var buttonObj = getElementObject(boxList[idx]+B_SFIX); + openBox(boxObj, buttonObj); + } +} + +/** + * Makes all the control buttons of boxes appear. + * + * @param boxList Array of box IDs + */ +function viewControlButtons(boxList) { + var idx; + for (idx = 0; idx < boxList.length; idx++) { + buttonObj = getElementObject(boxList[idx]+B_SFIX); + if (buttonObj != null) { + buttonObj.style.display = "inline"; + } + } +} + +/** + * Makes all the control buttons of boxes disappear. + * + * @param boxList Array of box IDs + */ +function hideControlButtons(boxList) { + var idx; + for (idx = 0; idx < boxList.length; idx++) { + buttonObj = getElementObject(boxList[idx]+B_SFIX); + if (buttonObj != null) { + buttonObj.style.display = "none"; + } + } +} + +/** + * Sets the page for either printing mode + * or viewing mode. In printing mode, the page + * is made to be more readable when printing it out. + * In viewing mode, the page is more browsable. + * + * @param isPrinterVersion If true, display in + * printing mode; otherwise, + * in viewing mode + */ +function displayMode(isPrinterVersion) { + var obj; + if (isPrinterVersion) { + // Hide global control buttons + obj = getElementObject("globalControls"); + if (obj != null) { + obj.style.visibility = "hidden"; + } + // Hide Legend + obj = getElementObject("legend"); + if (obj != null) { + obj.style.display = "none"; + } + obj = getElementObject("legendTOC"); + if (obj != null) { + obj.style.display = "none"; + } + // Hide Glossary + obj = getElementObject("glossary"); + if (obj != null) { + obj.style.display = "none"; + } + obj = getElementObject("glossaryTOC"); + if (obj != null) { + obj.style.display = "none"; + } + + // Expand all XML Instance Representation tables + expandAll(xiBoxes); + // Expand all Schema Component Representation tables + expandAll(scBoxes); + + // Hide Control buttons + hideControlButtons(xiBoxes); + hideControlButtons(scBoxes); + } else { + // View global control buttons + obj = getElementObject("globalControls"); + if (obj != null) { + obj.style.visibility = "visible"; + } + // View Legend + obj = getElementObject("legend"); + if (obj != null) { + obj.style.display = "block"; + } + obj = getElementObject("legendTOC"); + if (obj != null) { + obj.style.display = "block"; + } + // View Glossary + obj = getElementObject("glossary"); + if (obj != null) { + obj.style.display = "block"; + } + obj = getElementObject("glossaryTOC"); + if (obj != null) { + obj.style.display = "block"; + } + + // Expand all XML Instance Representation tables + expandAll(xiBoxes); + // Collapse all Schema Component Representation tables + collapseAll(scBoxes); + + // View Control buttons + viewControlButtons(xiBoxes); + viewControlButtons(scBoxes); + } +} + +/** + * Opens up a window displaying the documentation + * of a schema component in the XML Instance + * Representation table. + * + * @param compDesc Description of schema component + * @param compName Name of schema component + * @param docTextArray Array containing the paragraphs + * of the new document + */ +function viewDocumentation(compDesc, compName, docTextArray) { + var width = 400; + var height = 200; + var locX = 100; + var locY = 200; + + /* Generate content */ + var actualText = "<html>"; + actualText += "<head><title>"; + actualText += compDesc; + if (compName != '') { + actualText += ": " + compName; + } + actualText += "</title></head>"; + actualText += "<body bgcolor=\"#FFFFEE\">"; + // Title + actualText += "<p style=\"font-family: Arial, sans-serif; font-size: 12pt; font-weight: bold; letter-spacing:1px;\">"; + actualText += compDesc; + if (compName != '') { + actualText += ": <span style=\"color:#006699\">" + compName + "</span>"; + } + actualText += "</p>"; + // Documentation + var idx; + for (idx = 0; idx < docTextArray.length; idx++) { + actualText += "<p style=\"font-family: Arial, sans-serif; font-size: 10pt;\">" + docTextArray[idx] + "</p>"; + } + // Link to close window + actualText += "<a href=\"javascript:void(0)\" onclick=\"window.close();\" style=\"font-family: Arial, sans-serif; font-size: 8pt;\">Close</a>"; + actualText += "</body></html>"; + + /* Display window */ + windowCount++; + var docWindow = window.open("", "documentation"+windowCount, "toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable,alwaysRaised,dependent,titlebar=no,width="+width+",height="+height+",screenX="+locX+",left="+locX+",screenY="+locY+",top="+locY); + docWindow.document.write(actualText); +} + + + + + + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* More-configurable styles */ + +/******** General ********/ + +/* Document body */ +body { + color: Black; + background-color: White; + font-family: Arial, sans-serif; + font-size: 10pt; +} +/* Horizontal rules */ +hr { + color: black; +} +/* Document title */ +h1 { + font-size: 18pt; + letter-spacing: 2px; + border-bottom: 1px #ccc solid; + padding-top: 5px; + padding-bottom: 5px; +} +/* Main section headers */ +h2 { + font-size: 14pt; + letter-spacing: 1px; +} +/* Sub-section headers */ +h3, h3 a, h3 span { + font-size: 12pt; + font-weight: bold; + color: black; +} +/* Table displaying the properties of the schema components or the + schema document itself */ +table.properties th, table.properties th a { + color: black; + background-color: #F99; /* Pink */ +} +table.properties td { + background-color: #eee; /* Gray */ +} + + +/******** Table of Contents Section ********/ + +/* Controls for switching between printing and viewing modes */ +div#printerControls { + color: #963; /* Orange-brown */ +} +/* Controls that can collapse or expand all XML Instance + Representation and Schema Component Representation boxes */ +div#globalControls { + border: 2px solid #999; +} + + +/******** Schema Document Properties Section ********/ + +/* Table displaying the namespaces declared in the schema */ +table.namespaces th { + background-color: #ccc; +} +table.namespaces td { + background-color: #eee; +} +/* Target namespace of the schema */ +span.targetNS { + color: #06C; + font-weight: bold; +} + + +/******** Schema Components' Sections ********/ + +/* Name of schema component */ +.name { + color: #F93; /* Orange */ +} + +/* Hierarchy table */ +table.hierarchy { + border: 2px solid #999; /* Gray */ +} + +/* XML Instance Representation table */ +div.sample div.contents { + border: 2px dashed black; +} + +/* Schema Component Representation table */ +div.schemaComponent div.contents { + border: 2px black solid; +} + + +/******** Glossary Section ********/ + +/* Glossary Terms */ +.glossaryTerm { + color: #036; /* Blue */ +} + + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* Printer-version styles */ + +@media print { + +/* Ensures that controls are hidden when printing */ +div#printerControls { + visibility: hidden; +} +div#globalControls { + visibility: hidden; +} +#legend { + display: none; +} +#legendTOC { + display: none; +} +#glossary { + display: none; +} +#glossaryTOC { + display: none; +} + +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* Base styles */ + +/******** General ********/ + +/* Unordered lists */ +ul { + margin-left: 1.5em; + margin-bottom: 0em; +} +/* Tables */ +table { + margin-top: 10px; + margin-bottom: 10px; + margin-left: 2px; + margin-right: 2px; +} +table th, table td { + font-size: 10pt; + vertical-align: top; + padding-top: 3px; + padding-bottom: 3px; + padding-left: 10px; + padding-right: 10px; +} +table th { + font-weight: bold; + text-align: left; +} +/* Table displaying the properties of the schema components or the + schema document itself */ +table.properties { + width: 90%; +} +table.properties th { + width: 30%; +} +/* Boxes that can make its content appear and disappear*/ +div.box { + margin: 1em; +} + /* Box caption */ +div.box span.caption { + font-weight: bold; +} + /* Button to open and close the box */ +div.box input.control { + width: 1.4em; + height: 1.4em; + text-align: center; + vertical-align: middle; + font-size: 11pt; +} + /* Box contents */ +div.box div.contents { + margin-top: 3px; +} + + +/******** Table of Contents Section ********/ + +/* Controls for switching between printing and viewing modes */ +div#printerControls { + white-space: nowrap; + font-weight: bold; + padding: 5px; + margin: 5px; +} +/* Controls that can collapse or expand all XML Instance + Representation and Schema Component Representation boxes */ +div#globalControls { + padding: 10px; + margin: 5px; +} + + +/******** Schema Document Properties Section ********/ + +/* Table displaying the namespaces declared in the schema */ +table.namespaces th { +} +table.namespaces td { +} +/* Target namespace of the schema */ +span.targetNS { +} + + +/******** Schema Components' Sections ********/ + +/* Name of schema component */ +.name { +} + +/* Hierarchy table */ +table.hierarchy { + width: 90%; +} +table.hierarchy th { + font-weight: normal; + font-style: italic; + width: 20%; +} +table.hierarchy th, table.hierarchy td { + padding: 5px; +} + +/* XML Instance Representation table */ +div.sample { + width: 90%; +} +div.sample div.contents { + padding: 5px; + font-family: Courier New, sans-serif; + font-size: 10pt; +} + /* Normal elements and attributes */ +div.sample div.contents, div.sample div.contents a { + color: black; +} + /* Group Headers */ +div.sample div.contents .group, div.sample div.contents .group a { + color: #999; /* Light gray */ +} + /* Type Information */ +div.sample div.contents .type, div.sample div.contents .type a { + color: #999; /* Light gray */ +} + /* Occurrence Information */ +div.sample div.contents .occurs, div.sample div.contents .occurs a { + color: #999; /* Light gray */ +} + /* Fixed values */ +div.sample div.contents .fixed { + color: #063; /* Green */ + font-weight: bold; +} + /* Simple type constraints */ +div.sample div.contents .constraint, div.sample div.contents .constraint a { + color: #999; /* Light gray */ +} + /* Elements and attributes inherited from base type */ +div.sample div.contents .inherited, div.sample div.contents .inherited a { + color: #666; /* Dark gray */ +} + /* Elements and attributes added to or changed from base type */ +div.sample div.contents .newFields { + font-weight: bold; +} + /* Other type of information */ +div.sample div.contents .other, div.sample div.contents .other a { + color: #369; /* Blue */ + font-style: italic; +} + /* Link to open up window displaying documentation */ +div.sample div.contents a.documentation { + text-decoration: none; + padding-left: 3px; + padding-right: 3px; + padding-top: 0px; + padding-bottom: 0px; + font-weight: bold; + font-size: 11pt; + background-color: #FFD; + color: #069; +} + /* Invert colors when hovering over link to open up window + displaying documentation */ +div.sample div.contents a.documentation:hover { + color: #FFD; + background-color: #069; +} + +/* Schema Component Representation table */ +div.schemaComponent { + width: 90%; +} +div.schemaComponent div.contents { + font-family: Courier New, sans-serif; + font-size: 10pt; + padding: 5px; +} + /* Syntax characters */ +div.schemaComponent div.contents { + color: #00f; /* blue */ +} + /* Element and attribute tags */ +div.schemaComponent div.contents .scTag { + color: #933; /* maroon */ +} + /* Element and attribute content */ +div.schemaComponent div.contents .scContent, div.schemaComponent div.contents .scContent a { + color: black; + font-weight: bold; +} + /* Comments */ +div.schemaComponent div.contents .comment { + color: #999; /* Light gray */ +} + +/******** Legend Section ********/ + +div#legend table, div#legend div { + margin-bottom: 3px; +} +div#legend div.hint { + color: #999; /* Light gray */ + width: 90%; + margin-left: 1em; + margin-bottom: 2em; +} + + +/******** Glossary Section ********/ + +/* Glossary Terms */ +.glossaryTerm { + font-weight: bold; +} + + +/******** Footer ********/ + +.footer { + font-size: 8pt; +} + + + + + + +
    +

    Complex Type:

    +
    Schema Component Type
    +
    +
    +

    AusAddress

    +
    Schema Component Name
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Super-types: + Address + < + AusAddress + (by extension) + Parent type: + Address + (derivation method: extension) +
    Sub-types: +
      +
    • + QLDAddress + (by restriction) +
    • +
    +
    Direct sub-types: +
      +
    • + QLDAddress + (by restriction) +
    • +
    +
    +
    If this schema component is a type definition, its type hierarchy is shown in a gray-bordered box.
    + + + + + + + + + + + +
    NameAusAddress
    + Abstract + no
    +
    The table above displays the properties of this schema component.
    + + +
    +
    + XML Instance Representation +
    +
    + <... + + country="Australia" + + >
    + <unitNo> string </unitNo> [0..1]
    + <houseNo> string </houseNo> [1]
    + <street> string </street> [1]
    + Start Choice [1]
    + <city> string </city> [1]
    + <town> string </town> [1]
    + End Choice
    + + <state> AusStates </state> [1]
    + <postcode> string <<pattern = [1-9][0-9]{3}>> </postcode> [1] + + ? + +
    +
    + </...>
    +
    +
    +
    +

    The XML Instance Representation table above shows the schema component's content as an XML instance.

    +
      +
    • The minimum and maximum occurrence of elements and attributes are provided in square brackets, e.g. [0..1].
    • +
    • Model group information are shown in gray, e.g. Start Choice ... End Choice.
    • +
    • For type derivations, the elements and attributes that have been added to or changed from the base type's content are shown in bold.
    • +
    • If an element/attribute has a fixed value, the fixed value is shown in green, e.g. country="Australia".
    • +
    • Otherwise, the type of the element/attribute is displayed. +
        +
      • If the element/attribute's type is in the schema, a link is provided to it.
      • + +
      • For local simple type definitions, the constraints are displayed in angle brackets, e.g. <<pattern = [1-9][0-9]{3}>>.
      • +
      +
    • + +
    • If a local element/attribute has documentation, it will be displayed in a window that pops up when the question mark inside the attribute or next to the element is clicked, e.g. <postcode>.
    • +
      +
    +
    + + +
    +
    + Schema Component Representation +
    +
    + <complexType name="AusAddress">
    + <complexContent>
    + <extension base="Address">
    + <sequence>
    + <element name="state" type="AusStates"/>
    + <element name="postcode">
    + <simpleType>
    + <restriction base="string">
    + <pattern value="[1-9][0-9]{3}"/>
    + </restriction>
    + </simpleType>
    + </element>
    + </sequence>
    + <attribute name="country" type="string" fixed="Australia"/>
    + </extension>
    + </complexContent>
    + </complexType>
    +
    +
    +
    The Schema Component Representation table above displays the underlying XML representation of the schema component. (Annotations are not shown.)
    +
    + + + + + Abstract + Abstract + + (Applies to complex type definitions and element declarations). + An abstract element or complex type cannot used to validate an element instance. + If there is a reference to an abstract element, only element declarations that can substitute the abstract element can be used to validate the instance. + For references to abstract type definitions, only derived types can be used. + + + + + All + All Model Group + + Child elements can be provided + + in any order + + in instances. + + http://www.w3.org/TR/xmlschema-1/#element-all + + + + Choice + Choice Model Group + + + Only one + + from the list of child elements and model groups can be provided in instances. + + http://www.w3.org/TR/xmlschema-1/#element-choice + + + + CollapseWS + Collapse Whitespace Policy + Replace tab, line feed, and carriage return characters with space character (Unicode character 32). Then, collapse contiguous sequences of space characters into single space character, and remove leading and trailing space characters. + + + + ElemBlock + Disallowed Substitutions + + (Applies to element declarations). + If + substitution + is specified, then + + SubGroup + substitution group + + members cannot be used in place of the given element declaration to validate element instances. + + If + derivation methods + , e.g. extension, restriction, are specified, then the given element declaration will not validate element instances that have types derived from the element declaration's type using the specified derivation methods. + Normally, element instances can override their declaration's type by specifying an + xsi:type + attribute. + + + + + Key + Key Constraint + + Like + + Unique + Uniqueness Constraint + + , but additionally requires that the specified value(s) must be provided. + + http://www.w3.org/TR/xmlschema-1/#cIdentity-constraint_Definitions + + + + KeyRef + Key Reference Constraint + + Ensures that the specified value(s) must match value(s) from a + + Key + Key Constraint + + or + + Unique + Uniqueness Constraint + + . + + http://www.w3.org/TR/xmlschema-1/#cIdentity-constraint_Definitions + + + + ModelGroup + Model Group + + Groups together element content, specifying the order in which the element content can occur and the number of times the group of element content may be repeated. + + http://www.w3.org/TR/xmlschema-1/#Model_Groups + + + + Nillable + Nillable + + (Applies to element declarations). + If an element declaration is nillable, instances can use the + xsi:nil + attribute. + The + xsi:nil + attribute is the boolean attribute, + nil + , from the + http://www.w3.org/2001/XMLSchema-instance + namespace. + If an element instance has an + xsi:nil + attribute set to true, it can be left empty, even though its element declaration may have required content. + + + + + Notation + Notation + A notation is used to identify the format of a piece of data. Values of elements and attributes that are of type, NOTATION, must come from the names of declared notations. + http://www.w3.org/TR/xmlschema-1/#cNotation_Declarations + + + + PreserveWS + Preserve Whitespace Policy + Preserve whitespaces exactly as they appear in instances. + + + + TypeFinal + Prohibited Derivations + + (Applies to type definitions). + Derivation methods that cannot be used to create sub-types from a given type definition. + + + + + TypeBlock + Prohibited Substitutions + + (Applies to complex type definitions). + Prevents sub-types that have been derived using the specified derivation methods from validating element instances in place of the given type definition. + + + + + ReplaceWS + Replace Whitespace Policy + Replace tab, line feed, and carriage return characters with space character (Unicode character 32). + + + + Sequence + Sequence Model Group + + Child elements and model groups must be provided + + in the specified order + + in instances. + + http://www.w3.org/TR/xmlschema-1/#element-sequence + + + + SubGroup + Substitution Group + + Elements that are + + members + + of a substitution group can be used wherever the + + head + + element of the substitution group is referenced. + + + + + ElemFinal + Substitution Group Exclusions + + (Applies to element declarations). + Prohibits element declarations from nominating themselves as being able to substitute a given element declaration, if they have types that are derived from the original element's type using the specified derivation methods. + + + + + TargetNS + Target Namespace + The target namespace identifies the namespace that components in this schema belongs to. If no target namespace is provided, then the schema components do not belong to any namespace. + + + + Unique + Uniqueness Constraint + Ensures uniqueness of an element/attribute value, or a combination of values, within a specified scope. + http://www.w3.org/TR/xmlschema-1/#cIdentity-constraint_Definitions + + + + + + + + + + +

    + + + + + + + See: + + + + . + +

    +
    + + + + + + + + +
      + + + +
    +
    + + + true + + + + + + + + +
    +
      + + +
    • + This element can be used wherever the following element is referenced: +
        +
      • + + + +
      • +
      +
    • +
      + + +
    • + The following elements can be used wherever this element is referenced: + +
    • +
      +
    +
    +
    +
    + + + + + + + + + + + + + + +
    + + + Super-types: + + + Parent type: + + + + + + + + + + + None + + +
    + + + Sub-types: + + + Direct sub-types: + + + + + + +
    +
    + + + + + + + + + + + + + + +
    + + + Super-types: + + + Parent type: + + + + + + + + + + + None + + +
    + + + Sub-types: + + + Direct sub-types: + + + + + + +
    +
    + + + + + + + + + + + + + +
  • + + false + + Circular element reference to: + + + +
  • +
    + + + + + + + + + + + + + + + + + + +
  • + + + +
  • + + + + + + + +
    +
    +
    +
    + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Local type definition + + + + + + + + + + + + + + extension + + + restriction + + + + + + restriction + + + + + + + + + + + + < + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + < + + + + + + + + + + + + + + + + + + + (by + + ) + + + + + + + + + + + + + + + + + + + + + (derivation method: + + ) + + + + + + + + + + + true + + + + + + + + + + + +
      + +
    • + + + + + + + (by + + ) + + + + + false + + + +
    • +
      +
    +
    + + + + + + + None + + +
    +
    +
    + + + + + true + + + + + + + + + + + +
      + +
    • + + + + + + + (by restriction) + + + + + false + + + +
    • +
      +
    +
    + + +
      + +
    • + + + + + + + (by + + ) + + + + + false + + + +
    • +
      +
    +
    + + + + true + + + + + true + + + + + + + + + + + + + + None + + +
    +
    +
    + + + + + + + + + + + + + + + + + + +
    + More information at: + + + + . +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Name
    Type + + + Locally-defined simple type + + + + + + + + anySimpleType + + +
    Default Value
    Fixed Value
    +
    + + + + + + + + + + + + + +
    Name
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Name
    + + Abstract + Abstract + + + + + +
    + + TypeFinal + Prohibited Derivations + +
    + + TypeBlock + Prohibited Substitutions + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Name
    Type + + + Locally-defined simple type + + + Locally-defined complex type + + + + + + + + anyType + + +
    + + Nillable + Nillable + + + + + +
    + + Abstract + Abstract + + + + + +
    Default Value
    Fixed Value
    + + ElemFinal + Substitution Group Exclusions + +
    + + ElemBlock + Disallowed Substitutions + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Name
    Public Identifier
    System Identifier
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + TargetNS + Target Namespace + + + + + + + + + + None + + +
    Version
    Language
    Element and Attribute Namespaces +
      +
    • Global element and attribute declarations belong to this schema's target namespace.
    • +
    • + + + By default, local element declarations belong to this schema's target namespace. + + + By default, local element declarations have no namespace. + + +
    • +
    • + + + By default, local attribute declarations belong to this schema's target namespace. + + + By default, local attribute declarations have no namespace. + + +
    • +
    +
    Schema Composition +
      + + +
    • + This schema imports schema(s) from the following namespace(s): +
        + +
      • + + + (at + + + + ) + +
      • +
        +
      +
    • +
      + + +
    • + This schema includes components from the following schema document(s): +
        + +
      • + + + +
      • +
        +
      +
    • +
      + + +
    • + This schema includes components from the following schema document(s), where some of the components have been redefined: +
        + +
      • + + + +
      • +
        +
      + See Redefined Schema Components section. +
    • +
      +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Name
    Content + + + +
    + + TypeFinal + Prohibited Derivations + +
    +
    + + + + + + + + + + + Documentation + + +

    + +
    + + +
    + + + Application Data + + +

    + +
    + + +
    +
    + + + + + + + + + + + + + + + + +
    • + List of: + + + + + + + + + +
        +
      • + Locally defined type: + + + + +
      • +
      +
      +
      +
    +
    + + +
    • + Union of following types: +
        + + + + + type + true + + + + +
      • + Locally defined type: + + + + +
      • +
        +
      +
    +
    +
    +
    + + + + + + + + + + + + +
  • + + false + + Circular type reference to ' + + ' in type hierarchy. + + +
  • +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    • + Base XSD Type: + + + + + + + + + + + + +
    +
    + + + + + + + + + + + + + +
    • + ' + + ' super type was not found in this schema. + Its facets could not be printed out. +
    +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      + +
    • + +
    • +
      + +
    • + +
    • +
      + +
    • + +
    • +
      + +
    • + +
    • +
      + +
    • + +
    • +
      + +
    • + +
    • +
      + +
    • + +
    • +
      +
    +
    +
    + + + + + + + + + + + + + + + + + + + sample + XML Instance Representation + + + + true + + + + + + + 0 + false + false + this + + + + + + Start + + All + All + + + + + + + + + + +
    + + + + + + + + + + + + + End All +
    +
    +
    + + + + 0 + +
    + + + + attribute + element + + + + + + + +
    +
    + + + + + false + false + 0 + false + this + + + + + + + + + + + + + + + + +
    + + + + + newFields + + + inherited + + + + + + + + + + + + + + + =" + + + + + + + + + + + + + + + + + + + + + + + anySimpleType + + + + + + + + + + + + + + + + + " + +
    +
    +
    + + + + + false + false + 0 + false + this + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + newFields + + + inherited + + + + + + + + + =" + + + + + + + + + + + + + + + + + " + +
    +
    +
    + + + + this + + + + + + true + + + false + + + + + + + + + + + + + + + false + false + 0 + + this + + + + + + + + + + + + + false + + Circular attribute group reference: + + + + + + + + + + attribute group + + + + + + +
    + Attribute group reference (not shown): + + + + + + + + +
    +
    + + + + + + + + + + true + + + + + + + + + + + + + true + + + +
    +
    +
    +
    + + + + 0 + false + false + + this + + + + + + Start + + Choice + Choice + + + + + + + + + + +
    + + + + + + + + + + + + + + End Choice +
    +
    +
    + + + + this + + + + + + + + + + 0 + false + false + this + + + + + + + + + + + + + + + + + + + + + + + complex type + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + false + false + this + + + + + + + + + + + + + + + this + + + + + + + + + + + + 0 + false + false + + this + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Circular model group reference: + + + + + + + + +
    +
    + + + + + + group + + + + + + +
    + Model group reference (not shown): + + + + + + + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + + + + 0 + false + false + + this + + + + + + + + + + + + + + + Start + + Sequence + Sequence + + + + + + + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + End Sequence +
    +
    +
    +
    + + + + this + + + + + + + + + + + + this + + + + + + + + + + + + 0 + this + +
    + <!--
    + + + + + Unique + Uniqueness + + + + + Key + Key + + + + + KeyRef + Key Reference + + + + Constraint - + + + + + + + + + + + + + + +
    + + Selector - + + +
    + + Field(s) - + + + , + + + + + +
    + + + Refers to - + + + + +
    +
    + + --> +
    +
    + + + + + + + + + + + + + + + true + +A local schema component contains two dashes in +'documentation' elements within its 'annotation' element. + + + + + + , + + ' + + + More information at: + + + + . + + + + + + + + ' + + + + + + + + docArray = new Array( + + ); viewDocumentation(' + + + + ', ' + + + + + + + + + + + ', docArray); + + ? + + + + + + + + + + + + ' + \' + + + + + + " + \" + + + + + + + + 0 + false + false + + + + + + this + + + + + + + Start Group: + + + + + + + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + End Group: + +
    +
    +
    + + + + + 0 + false + false + this + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + newFields + + + inherited + + + + + < + + > + + + + + + + + + + + + + + + + + + + + + + + + + ... + + + + + + + + + + + + + + </ + + > + + + + + + + + + + + + +
    +
    + + + + + + 0 + false + false + this + + + + + + + + + + + + + + + + + + + + + + + ... + + + + + + + + true + + + false + + + + +
    + + + newFields + + + inherited + + + + + < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + + + + + + true + + + + + + + + + /> + + + + + + + + + + + + + + + + + > + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + <!-- Restricts : + + + + + --> + +
    + + +
    + <!-- Extends : + + + + + --> + +
    +
    + + + +
    + <!-- Mixed content --> + +
    + + + + + + </ + + > +
    +
    +
    +
    +
    +
    + + + + + + false + false + false + 0 + this + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + false + 0 + this + + + + + + + + + + + + + + + + + + + complex type + + + + + + + + + + + + + true + + + false + + + + + + + + + + + + + + + + + + + + + + + + true + + + false + + + + + + + + + + + + + + + + + + + + + true + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + 0 + this + + + + + + + true + + + + + + + + 0 + false + false + false + true + this + + +
    + + + + + + + + + + + + + + + + + + + complex type + + + + + + +
    + <!-- ' + + + + + ' super type was not found in this schema. Some elements and attributes may be missing. --> +
    +
    + + + + +
    + + + + + + + + + + + true + + + false + + + + + + + +
    + + + + + + + + + + + + +
    + <-- Extends: + + + + + (Circular type hierarchy) --> +
    +
    + + + + + + complex type + + + + + + + + + + + + + + true + + + false + + + + + + false + + + + + + +
    + <!-- ' + + + + + ' super type was not found in this schema. Some elements and attributes may be missing. --> +
    +
    + + + + + + + + + + true + + + false + + + + + + false + + + + +
    + + + + + + + + + + + true + + + false + + + + + + + +
    +
    +
    + + + + + + + + + +
    +
    + + + + + + + + + + +
    +
    + + + + + + + + + + + + +
    +
    + + + + + 0 + false + false + this + + + + + + + + + + + + + + + + + this + + + + + + + + + + + + + + + + list of: + + + + + + + list of: [ + + + + + ] + + + + + + union of: [ + + + + true + + + + + + type + , + + + + + + , + + [ + + + + + ] + + + ] + + + + + + + + this + + + + + + + + + + false + + Circular type reference to ' + + ' in type hierarchy. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ( + + + + ) + + + ( + + ) + + + ( + + ) + + + ( + + + + ) + + + ( + + + + ) + + + ( + + ) + + + ( + + + + ) + + + + + + + + + + + + + + + + + + schemaComponent + Schema Component Representation + + + + false + + + + + + 0 + + + + + + + + name + + + + + + type + + + + + + + + + + + *name+*type+ + + + *annotation+ + + + + + + 0 + + + + + + + + name + + + + + + *name+ + + + *annotation+ + + + + + + 0 + + + + + + + + ref + + + + + + + + + + *ref+ + + + *annotation+ + + + + + + 0 + + + + + + + + ref + + + + + + + + + + *ref+ + + + *annotation+ + + + + + + 0 + + + + + + + + ref + + + + + + + + + + *ref+ + + + *annotation+ + + + + + + 0 + + + + + + + + ref + + + + + + + + + + *ref+ + + + *annotation+ + + + + + + 0 + + + + + + + + + source + + + + + + + + + + + *source+ + + + true + + + + + + 0 + + + + + + + + name + + + + + refer + + + + + + + + + + + + *name+*refer+ + + + *annotation+ + + + + + + 0 + + + + + + + + + base + + + + + + + + + + + *base+ + + + *annotation+ + + + + + + 0 + + + + + + + + + itemType + + + + + + + + + + + *itemType+ + + + *annotation+ + + + + + + 0 + + + + + + + + + memberTypes + + + + type + + + + + + + + *memberTypes+ + + + *annotation+ + + + + + + 0 + + + + + + + + + xml:lang + + + + + + + *lang+ + + + *include+*import+*redefine+ + + + + + + 0 + + + + + + + + + + *annotation+ + + + + + + 0 + +
    + <-- + + --> +
    +
    + + + + + + 0 + false + + + + + + + + + true + + + + +
    + + < + + + + + + + + + + + + + +
    ...
    +
    + + + + + + + + +
    + +
    +
    + + + + + + +
    +
    + + + + + + > + + + + + + </ + + + + > + + + + + /> + + +
    +
    + + + + + + + + + + + =" + + + + + + " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + html + + + + + + + + + + + + + + xpp + + + + + + + + + + + +
    + <-- + + --> +
    +
    + + + + + xpp + + + < + + + + + + + + + + + + + =" + + " + + + + + + > + + + + + + +
    + +
    +
    +
    + + </ + + + + + + + + + > +
    + + + /> + +
    +
    + + + + + + + + + + + + + + + + + + [term] + + + + + + + + + + + + + + this + + + + + + + + + + + + + + + + + + + Unknown namespace prefix, + + . + + + + + + + + + + + + + + this + + + + + + attribute + + + + + + + attribute + + + + + + + + + + + this + + + + + + attribute group + + + + + + + attribute group + + + + + + + + + + + this + + + + + + element + + + + + + + element + + + + + + + + + + + this + + + + + + group + + + + + + + group + + + + + + + + + + + this + + + + + + uniqueness/key constraint + + + + + + + uniqueness/key constraint + + + + + + + + + + + this + + + + + + + type + + + + + + + + + type + + + + + + + + + + + + + this + + + + + + + declaration + + + definition + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "" + + + + + + + Jump to " + + " + + + + + + (located in external schema documentation) + + + + . + + + + + + + + javascript:void(0) + + + + + + + + + + + + + + + externalLink + + + + + + alert(' + + '); + + + + + + + + + + + + this + + + + + + declaration + + + definition + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + could not be found + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + xsd + + + + xml + + + + + + + + this + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + false + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + false + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + +
    +
    + + + + + + + + + + + + + + + + + + + +
    + + +
    + +
    + + + setState('', ); + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + All Model Group + + + Attribute + + + Attribute Group + + + Choice Model Group + + + Complex Type + + + Element + + + Model Group + + + Notation + + + Sequence Model Group + + + Simple Type + + + + true + +Unknown schema component, . + + + + + + + + + + + + + schema + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + +Unknown schema component, . + + + + + + + + + + + + + Notation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + +Documentation file for the schema at, , +was not specified in the links file, . + + + + + + + + + + + + + + yes + + + no + + + + + + + + false + this + + + + + + + + + + + + + + : + + + + + + + + + + + + + + + + 1 + + + 0 + + + + + + + + + + + + + 1 + + + + + + + + + + + + 0 + + + 1 + + + + + + + * + + + + + + + + + 1 + + + + + + + + + + [1] + + + [ + + .. + + ] + + + + + + + + + + + + restriction, extension, substitution + + + + + + + + + + + + + + restriction, extension + + + + + + + + + + + + + + restriction, list, union + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + true + this + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + false + + true + this + + + + + + + + + + + + + + + + + + +
  • + +
  • +
    + + + + + + + + + + + + + + +
    +
    + + + + element + + + + + + Allow any + + s from + + + + + any namespace + + + + a namespace other than this schema's namespace + + + + + + + true + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + no namespace + + + + + + and + + + , + + + this schema's namespace + + + + + + , and + + + and + + + the following namespace(s): + + + , + + + + + + ( + + + + + + strict + + + validation) + . + + + + + + + + + + + + + + + + + pattern + = + + + + + + + + + + total no. of digits + = + + + + + + + + + + + no. of fraction digits + + = + + + + + + + + + + value + comes from list: { + + + + | + + ' + + ' + + + } + + + + + + + + + + + length + + = + + + + + length + + >= + + + + + length + + <= + + + + + + + + + + + + + + Whitespace policy: + + PreserveWS + preserve + + + + Whitespace policy: + + ReplaceWS + replace + + + + Whitespace policy: + + CollapseWS + collapse + + + + + + + + + + + + + + + <= + + + + < + + + + value + + + + <= + + + + < + + + + + + + value + + >= + + + + + value + + > + + + + + value + + <= + + + + + value + + < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + XS3P ERROR: + + + + + + ERROR: + + + + + +
    \ No newline at end of file diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/aq/Acquisition.java b/ch.psi.fda/src/main/java/ch/psi/fda/aq/Acquisition.java new file mode 100644 index 0000000..36e598a --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/aq/Acquisition.java @@ -0,0 +1,1089 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.aq; + +import gov.aps.jca.CAException; + +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.FileHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter; + +import ch.psi.fda.core.ActionLoop; +import ch.psi.fda.core.Actor; +import ch.psi.fda.core.EngineConfiguration; +import ch.psi.fda.core.Sensor; +import ch.psi.fda.core.actions.ChannelAccessCondition; +import ch.psi.fda.core.actions.ChannelAccessPut; +import ch.psi.fda.core.actions.Delay; +import ch.psi.fda.core.actors.ChannelAccessFunctionActuator; +import ch.psi.fda.core.actors.ChannelAccessLinearActuator; +import ch.psi.fda.core.actors.ChannelAccessTableActuator; +import ch.psi.fda.core.actors.ComplexActuator; +import ch.psi.fda.core.actors.JythonFunction; +import ch.psi.fda.core.actors.OTFActuator; +import ch.psi.fda.core.actors.PseudoActuatorSensor; +import ch.psi.fda.core.collector.Collector; +import ch.psi.fda.core.collector.DataDispatcher; +import ch.psi.fda.core.guard.ChannelAccessGuard; +import ch.psi.fda.core.guard.ChannelAccessGuardCondition; +import ch.psi.fda.core.loops.ActorSensorLoop; +import ch.psi.fda.core.loops.OTFLoop; +import ch.psi.fda.core.loops.cr.CrlogicLoop; +import ch.psi.fda.core.loops.cr.ParallelCrlogic; +import ch.psi.fda.core.loops.cr.ScrlogicLoop; +import ch.psi.fda.core.manipulator.JythonManipulation; +import ch.psi.fda.core.manipulator.Manipulation; +import ch.psi.fda.core.manipulator.Manipulator; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.Message; +import ch.psi.fda.core.scripting.JythonGlobalVariable; +import ch.psi.fda.core.scripting.JythonGlobalVariableDictionary; +import ch.psi.fda.core.scripting.JythonParameterMapping; +import ch.psi.fda.core.scripting.JythonParameterMappingChannel; +import ch.psi.fda.core.scripting.JythonParameterMappingGlobalVariable; +import ch.psi.fda.core.scripting.JythonParameterMappingID; +import ch.psi.fda.core.sensors.ChannelAccessDoubleArraySensor; +import ch.psi.fda.core.sensors.ChannelAccessDoubleSensor; +import ch.psi.fda.core.sensors.ChannelAccessStringSensor; +import ch.psi.fda.core.sensors.MillisecondTimestampSensor; +import ch.psi.fda.core.sensors.OTFNamedChannelSensor; +import ch.psi.fda.core.sensors.OTFReadbackSensor; +import ch.psi.fda.core.sensors.OTFScalerChannelSensor; +import ch.psi.fda.model.ModelManager; +import ch.psi.fda.model.v1.Action; +import ch.psi.fda.model.v1.ArrayDetector; +import ch.psi.fda.model.v1.ArrayPositioner; +import ch.psi.fda.model.v1.ChannelAction; +import ch.psi.fda.model.v1.ChannelParameterMapping; +import ch.psi.fda.model.v1.Configuration; +import ch.psi.fda.model.v1.ContinuousDimension; +import ch.psi.fda.model.v1.ContinuousPositioner; +import ch.psi.fda.model.v1.Detector; +import ch.psi.fda.model.v1.DetectorOfDetectors; +import ch.psi.fda.model.v1.DiscreteStepDimension; +import ch.psi.fda.model.v1.DiscreteStepPositioner; +import ch.psi.fda.model.v1.Function; +import ch.psi.fda.model.v1.FunctionPositioner; +import ch.psi.fda.model.v1.Guard; +import ch.psi.fda.model.v1.GuardCondition; +import ch.psi.fda.model.v1.IDParameterMapping; +import ch.psi.fda.model.v1.LinearPositioner; +import ch.psi.fda.model.v1.ParameterMapping; +import ch.psi.fda.model.v1.Positioner; +import ch.psi.fda.model.v1.PseudoPositioner; +import ch.psi.fda.model.v1.Recipient; +import ch.psi.fda.model.v1.Region; +import ch.psi.fda.model.v1.RegionPositioner; +import ch.psi.fda.model.v1.ScalarDetector; +import ch.psi.fda.model.v1.ScalerChannel; +import ch.psi.fda.model.v1.Scan; +import ch.psi.fda.model.v1.ScriptAction; +import ch.psi.fda.model.v1.ScriptManipulation; +import ch.psi.fda.model.v1.ShellAction; +import ch.psi.fda.model.v1.SimpleScalarDetector; +import ch.psi.fda.model.v1.Timestamp; +import ch.psi.fda.model.v1.Variable; +import ch.psi.fda.model.v1.VariableParameterMapping; +import ch.psi.fda.notification.NotificationAgent; +import ch.psi.fda.serializer.DataSerializerTXT; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * Data acquisition engine for performing scans + * Mapping is specific to scan model version 1.0 + * @author ebner + * + */ +public class Acquisition { + + // Get Logger + private static Logger logger = Logger.getLogger(Acquisition.class.getName()); + + private AcquisitionEngineConfiguration configuration; + + private ActionLoop actionLoop; + private Collector collector; + private DataDispatcher dispatcher; + private Manipulator manipulator; + private DataSerializerTXT serializer; + + private List manipulations; + private volatile boolean active = false; + + private NotificationAgent notificationAgent; + + private Handler logHandler = null; + + /** + * Name of the datafile + */ + private File datafile; + +// private Thread acquisitionThread = null; + + public Acquisition(){ + configuration = AcquisitionEngineConfiguration.getInstance(); + actionLoop = null; + collector = new Collector(); + manipulations = new ArrayList(); + + } + + + + /** + * Get state of the acquisition engine + * @return the active + */ + public boolean isActive() { + return active; + } + + + + /** + * Acquire data + * + * @param smodel Model of the scan + * @param getQueue Flag whether to return a queue or not. If false the return value of the function will be null. + * @throws InterruptedException + */ + public DataQueue initalize(Configuration smodel, boolean getQueue) { + + // Create notification agent with globally configured recipients + notificationAgent = new NotificationAgent(configuration.getSmptServer(), "fda.notification@psi.ch"); + + // Update recipients list of the Notifiaction Agent + if(smodel.getNotification()!=null){ + for(Recipient r: smodel.getNotification().getRecipient()){ + notificationAgent.getRecipients().add(r); + } + } + + + Date date = new Date(); + // Prepare output directory / create directory if it does not exist + File bdir = new File(configuration.replaceMacros(configuration.getDataBaseDirectory(), date, smodel.getData().getFileName())); +// bdir.mkdirs(); + String fprefix = configuration.replaceMacros(configuration.getDataFilePrefix(), date, smodel.getData().getFileName()); + + // Construct filenames + File xmlfile = new File(bdir, fprefix+smodel.getData().getFileName()+".xml"); + datafile = new File(bdir, fprefix+smodel.getData().getFileName()+".txt"); + + // Create required directories + xmlfile.getParentFile().mkdirs(); + + try{ + // Workaround - to avoid that multiple handlers get attached + // this should be removed when rewriting the acquisition logic + if(logHandler!=null){ + Logger.getLogger("").removeHandler(logHandler); + } + + File logfile = new File(bdir, fprefix+smodel.getData().getFileName()+".log"); + logHandler = new FileHandler(logfile.getAbsolutePath()); + logHandler.setFormatter(new SimpleFormatter()); + Logger.getLogger("").addHandler(logHandler); + Logger.getLogger("ch.psi.fda").setLevel(Level.FINEST); + // Also include log messages of the channel access implementation + Logger.getLogger("com.cosylab.epics.caj").setLevel(Level.INFO); + } catch (SecurityException e1) { + e1.printStackTrace(); + } catch (IOException e1) { + e1.printStackTrace(); + } + + + // Save a copy of the model to the data directory + try { + ModelManager.marshall(smodel, xmlfile); + } catch (Exception e) { + throw new RuntimeException("Unable to serialize scan",e); + } + + // Configure core engine + EngineConfiguration.getInstance().setFailOnSensorError(smodel.isFailOnSensorError()); + + + logger.fine("Map Model to internal logic"); + + // Map scan to base model + // After this call actionLoop and collector will be initialized + mapScan(smodel); + + logger.fine("ActionLoop and Collector initialized"); + + // TODO Remove this workaround + Collections.reverse(collector.getQueues()); + + // Add manipulator into processing chain + this.manipulator = new Manipulator(collector.getOutQueue(), this.manipulations); + + // // Insert dispatcher into processing chain + this.dispatcher = new DataDispatcher(manipulator.getOutQueue()); + + DataQueue dq = new DataQueue(new LinkedBlockingQueue(1000), manipulator.getOutQueue().getDataMessageMetadata()); // Create bounded queue to + // prevent running out of + // memory ... + this.serializer = new DataSerializerTXT(dq, datafile, true); + + DataQueue vdq = null; + if (getQueue) { + vdq = new DataQueue(new LinkedBlockingQueue(1000), manipulator.getOutQueue().getDataMessageMetadata()); // Create bounded queue to prevent + // running out of memory ... + dispatcher.getOutQueues().add(vdq); + } + + // Add queue for serializer to dispatcher + dispatcher.getOutQueues().add(dq); + + return (vdq); + } + + /** + * Execute acquisition + * @throws InterruptedException + */ + public void execute() throws InterruptedException { + +// acquisitionThread = Thread.currentThread(); + + try{ + active = true; + + Thread tc = new Thread(collector); + tc.start(); + + Thread tm = new Thread(manipulator); + tm.start(); + + Thread td = new Thread(dispatcher); + td.start(); + + Thread t = new Thread(serializer); + t.start(); + + actionLoop.prepare(); + actionLoop.execute(); + actionLoop.cleanup(); + + // Wait for data collector threads + // Do this with a Latch or something like that + + // Give the threads 1 minute to catch up + tc.join(60000); + tm.join(60000); + td.join(60000); + t.join(60000); + + // Send notifications out to all recipients that want to have success notifications + try { + String hostname = InetAddress.getLocalHost().getHostName(); + notificationAgent.sendNotification("Notification - FDA Execution Finished", "The execution of the FDA on '"+hostname+"' for file '"+datafile.getName()+"' finished successfully\n\nYou received this message because you are listed in the notification list for this data acquisition configuration.", false,true); + } catch (UnknownHostException e1) { + logger.log(Level.WARNING, "Unable to send notification", e1); + } + +// active = false; + } + catch(RuntimeException e){ + logger.log(Level.WARNING, "Execution failed: ", e); + + try { + String hostname = InetAddress.getLocalHost().getHostName(); + notificationAgent.sendNotification("Notification - FDA Execution Failed", "The execution of the FDA failed on '"+hostname+"' for file '"+datafile.getName()+"'\n\nYou received this message because you are listed in the notification list for this data acquisition configuration.", true,false); + } catch (UnknownHostException e1) { + logger.log(Level.WARNING, "Unable to send notification", e1); + } + + throw e; + } + catch(InterruptedException e){ + logger.log(Level.WARNING, "Execution interrupted: ", e); + + // Execution got aborted. + try { + String hostname = InetAddress.getLocalHost().getHostName(); + notificationAgent.sendNotification("Notification - FDA Execution was aborted", "The execution of the FDA on '"+hostname+"' for file '"+datafile.getName()+"' was aborted\n\nYou received this message because you are listed in the notification list for this data acquisition configuration.", false, true); + } catch (UnknownHostException e1) { + logger.log(Level.WARNING, "Unable to send notification", e1); + } +// throw e; + + } + finally{ + active = false; + } + } + + + public void destroy(){ + if(actionLoop != null){ + logger.finest("Destroy action loop"); + try{ + actionLoop.destroy(); + } + catch(Exception e){ + logger.log(Level.SEVERE, "Unable to destroy action loop", e); + } + } + + // Clear global variables Jython + JythonGlobalVariableDictionary.getInstance().clear(); + + // Destroy the CA context + try { + logger.fine("Destroy Channel Access context"); + ChannelBeanFactory.getFactory().getChannelFactory().destroyContext(); + } catch (IllegalStateException e) { + logger.log(Level.SEVERE, "Unable to destroy channel access context", e); + } catch (CAException e) { + logger.log(Level.SEVERE, "Unable to destroy channel access context", e); + } + logger.fine("Context destroyed"); + + // Remove log handler + if(logHandler!=null){ + logger.fine("Close log handler"); + logHandler.close(); + Logger.getLogger("").removeHandler(logHandler); + } + } + + /** + * Abort acquisition + */ + public void abort(){ + + actionLoop.abort(); +// if(acquisitionThread!=null){ +// acquisitionThread.interrupt(); +// } + } + + public String getDatafileName(){ + return(datafile.getName()); + } + + /** + * Retrieve id string of the passed object + * @param object + * @return Id string of object + */ + private static String resolveIdRef(Object object){ + String id; + if(object instanceof Positioner){ + id = ((Positioner)object).getId(); + } + else if (object instanceof Detector){ + id = ((Detector)object).getId(); + } + else if (object instanceof ch.psi.fda.model.v1.Manipulation){ + id = ((ch.psi.fda.model.v1.Manipulation)object).getId(); + } + else{ + throw new RuntimeException("Unable to identify id of object reference "+object); + } + return id; + } + + + + /** + * Map scan to base model + * @param scan + */ + private void mapScan(Configuration configuration){ + Scan scan = configuration.getScan(); + + // Map continuous dimension + if(scan.getCdimension() != null){ + ActionLoop aLoop = mapContinuousDimension(scan.getCdimension()); + actionLoop = aLoop; + collector.getQueues().add(aLoop.getDataQueue()); + } + + // Map discrete step dimensions + for(DiscreteStepDimension d: scan.getDimension()){ + ActorSensorLoop l = mapDiscreteStepDimension(d); + collector.getQueues().add(l.getDataQueue()); + if(actionLoop != null){ + l.getActionLoops().add(actionLoop); + } + actionLoop = l; + } + + // No dimensions where specified for scan + if(actionLoop == null){ + actionLoop = new ActorSensorLoop(); + } + + // Map pre actions to pre actions of the top level dimension + actionLoop.getPreActions().addAll(mapActions(scan.getPreAction())); + + // Map post actions to post actions of the top level dimension + actionLoop.getPostActions().addAll(mapActions(scan.getPostAction())); + + + // Handle iterations by adding a pseudo dimension and setting the + // datagroup flag in the main loop + if(configuration.getNumberOfExecution()>1){ + // Create Iterations pseudo loop + ActorSensorLoop l = new ActorSensorLoop(); + PseudoActuatorSensor a = new PseudoActuatorSensor("iterations", configuration.getNumberOfExecution()); + l.getActors().add(a); + l.getActionLoops().add(actionLoop); + actionLoop.setDataGroup(true); // Need to add setDataGroup to ActionLoop interface + + // Set toplevel action loop + actionLoop = l; + collector.getQueues().add(l.getDataQueue()); + } + + + JythonGlobalVariableDictionary dict = JythonGlobalVariableDictionary.getInstance(); + for(ch.psi.fda.model.v1.Manipulation m : scan.getManipulation()){ + if(m instanceof ScriptManipulation){ + ScriptManipulation sm = (ScriptManipulation) m; + + List mapping = new ArrayList(); + for(ParameterMapping pm: sm.getMapping()){ + if(pm instanceof IDParameterMapping){ + String refid = resolveIdRef(((IDParameterMapping)pm).getRefid()); + mapping.add( new JythonParameterMappingID(pm.getVariable(), refid)); + } + else if(pm instanceof ChannelParameterMapping){ + ChannelParameterMapping cpm = (ChannelParameterMapping) pm; + if(cpm.getType().equals("String")){ + mapping.add( new JythonParameterMappingChannel(cpm.getVariable(), cpm.getChannel(), String.class)); + } + else if(cpm.getType().equals("Integer")){ + mapping.add( new JythonParameterMappingChannel(cpm.getVariable(), cpm.getChannel(), Integer.class)); + } + else if(cpm.getType().equals("Double")){ + mapping.add( new JythonParameterMappingChannel(cpm.getVariable(), cpm.getChannel(), Double.class)); + } + else{ + logger.warning("Channel type ["+cpm.getType()+"] is not supported for mapping"); + } + } + else if(pm instanceof VariableParameterMapping){ + VariableParameterMapping vp = (VariableParameterMapping) pm; + Variable v = (Variable)vp.getName(); + JythonGlobalVariable var = dict.getVariable(v.getName()); + var.setValue(v.getValue()); + mapping.add(new JythonParameterMappingGlobalVariable(vp.getVariable(), var)); + } + } + + JythonManipulation manipulation = new JythonManipulation(sm.getId(), sm.getScript(), mapping, sm.isReturnArray()); + this.manipulations.add(manipulation); + } + } + } + + /** + * Map a model action to base actions + * @param actions + * @return + */ + private List mapActions(List actions){ + List alist = new ArrayList(); + for(Action a: actions){ + if(a instanceof ChannelAction){ + ChannelAction ca = (ChannelAction) a; + + String operation = ca.getOperation(); // Default = put + String type=ca.getType(); // Default = String + + if(operation.equals("put")){ + Long timeout = null; + if(ca.getTimeout()!=null){ + timeout = Math.round(ca.getTimeout()*1000); + } + if(type.equals("String")){ + alist.add(new ChannelAccessPut(ca.getChannel(), ca.getValue(), false, timeout)); + } + else if(type.equals("Integer")){ + alist.add(new ChannelAccessPut(ca.getChannel(), new Integer(ca.getValue()), false, timeout)); + } + else if(type.equals("Double")){ + alist.add(new ChannelAccessPut(ca.getChannel(), new Double(ca.getValue()), false, timeout)); + } + } + else if(operation.equals("putq")){ + if(type.equals("String")){ + alist.add(new ChannelAccessPut(ca.getChannel(), ca.getValue(), true, null)); + } + else if(type.equals("Integer")){ + alist.add(new ChannelAccessPut(ca.getChannel(), new Integer(ca.getValue()), true, null)); + } + else if(type.equals("Double")){ + alist.add(new ChannelAccessPut(ca.getChannel(), new Double(ca.getValue()), true, null)); + } + } + else if(operation.equals("wait")){ + Long timeout = null ; // Default timeout = wait forever + if(ca.getTimeout()!=null){ + timeout = Math.round(ca.getTimeout()*1000); + } + if(type.equals("String")){ + alist.add(new ChannelAccessCondition(ca.getChannel(), ca.getValue(), timeout)); + } + else if(type.equals("Integer")){ + alist.add(new ChannelAccessCondition(ca.getChannel(), new Integer(ca.getValue()), timeout)); + } + else if(type.equals("Double")){ + alist.add(new ChannelAccessCondition(ca.getChannel(), new Double(ca.getValue()), timeout)); + } + } + else{ + // Operation not supported + logger.warning("Operation "+operation+" for action is not supported"); + } + + // Translate delay attribute to delay action + if(ca.getDelay()!=null){ + Double x = ca.getDelay()*1000; + alist.add(new Delay(x.longValue())); + } + + } + else if(a instanceof ShellAction){ + ShellAction sa = (ShellAction) a; + alist.add(new ch.psi.fda.core.actions.ShellAction(sa.getCommand())); + } + else if(a instanceof ScriptAction){ + + ScriptAction sa = (ScriptAction) a; + // TODO create Jython Action + List mapping = new ArrayList(); + for(ChannelParameterMapping ma: sa.getMapping()){ + if(ma.getType().equals("String")){ + mapping.add(new JythonParameterMappingChannel(ma.getVariable(), ma.getChannel(), String.class)); + } + else if(ma.getType().equals("Integer")){ + mapping.add(new JythonParameterMappingChannel(ma.getVariable(), ma.getChannel(), Integer.class)); + } + else if(ma.getType().equals("Double")){ + mapping.add(new JythonParameterMappingChannel(ma.getVariable(), ma.getChannel(), Double.class)); + } + else{ + logger.warning("Channel type ["+ma.getType()+"] is not supported for mapping"); + } + } + alist.add(new ch.psi.fda.core.actions.JythonAction(sa.getScript(), mapping)); + } + } + return(alist); + } + + /** + * Map a discrete step dimension onto a actor sensor loop + * @param dimension + * @return + */ + private ActorSensorLoop mapDiscreteStepDimension(DiscreteStepDimension dimension){ + ActorSensorLoop aLoop = new ActorSensorLoop(dimension.isZigzag()); + // Set split flag of action loop (default is false) + aLoop.setDataGroup(dimension.isDataGroup()); + + // Mapping dimension pre-actions + aLoop.getPreActions().addAll(mapActions(dimension.getPreAction())); + + Long moveTimeout = AcquisitionEngineConfiguration.getInstance().getActorMoveTimeout(); + + // Mapping positioners + Double stime = 0d; + for(DiscreteStepPositioner p: dimension.getPositioner()){ + + if(p.getSettlingTime()>stime){ + stime = p.getSettlingTime(); + } + + if(p instanceof LinearPositioner){ + LinearPositioner lp =(LinearPositioner) p; + ChannelAccessLinearActuator a; + if(lp.getType().equals("String")){ + a = new ChannelAccessLinearActuator(lp.getName(), lp.getDone(), lp.getDoneValue(), lp.getDoneDelay(), lp.getStart(), lp.getEnd(), lp.getStepSize(), moveTimeout); + } + else if(lp.getType().equals("Double")){ + a = new ChannelAccessLinearActuator(lp.getName(), lp.getDone(), Double.parseDouble(lp.getDoneValue()), lp.getDoneDelay(), lp.getStart(), lp.getEnd(), lp.getStepSize(), moveTimeout); + } + else{ + // Default + a = new ChannelAccessLinearActuator(lp.getName(), lp.getDone(), Integer.parseInt(lp.getDoneValue()), lp.getDoneDelay(), lp.getStart(), lp.getEnd(), lp.getStepSize(), moveTimeout); + } + + a.setAsynchronous(lp.isAsynchronous()); + Actor actuator = a; + + aLoop.getActors().add(actuator); + + // Add a sensor for the readback + String name = lp.getReadback(); + if(name==null){ + name = lp.getName(); + } + ChannelAccessDoubleSensor sensor = new ChannelAccessDoubleSensor(lp.getId(), name); + aLoop.getSensors().add(sensor); + } + else if(p instanceof FunctionPositioner){ + FunctionPositioner lp =(FunctionPositioner) p; + + // Create function object + JythonFunction function = mapFunction(lp.getFunction()); + + + // Create actuator + ChannelAccessFunctionActuator a; + if(lp.getType().equals("String")){ + a = new ChannelAccessFunctionActuator(lp.getName(), lp.getDone(), lp.getDoneValue(), lp.getDoneDelay(), function, lp.getStart(), lp.getEnd(), lp.getStepSize(), moveTimeout); + } + else if(lp.getType().equals("Double")){ + a = new ChannelAccessFunctionActuator(lp.getName(), lp.getDone(), Double.parseDouble(lp.getDoneValue()), lp.getDoneDelay(), function, lp.getStart(), lp.getEnd(), lp.getStepSize(), moveTimeout); + } + else{ + // Default + a = new ChannelAccessFunctionActuator(lp.getName(), lp.getDone(), Integer.parseInt(lp.getDoneValue()), lp.getDoneDelay(), function, lp.getStart(), lp.getEnd(), lp.getStepSize(), moveTimeout); + } + + a.setAsynchronous(lp.isAsynchronous()); + Actor actuator = a; + + aLoop.getActors().add(actuator); + + // Add a sensor for the readback + String name = lp.getReadback(); + if(name==null){ + name = lp.getName(); + } + ChannelAccessDoubleSensor sensor = new ChannelAccessDoubleSensor(lp.getId(), name); + aLoop.getSensors().add(sensor); + } + else if (p instanceof ArrayPositioner){ + ArrayPositioner ap = (ArrayPositioner) p; + String[] positions = (ap.getPositions().trim()).split(" +"); + double[] table = new double[positions.length]; + for(int i=0;i a; + if(p.getType().equals("String")){ + a = new ChannelAccessTableActuator(p.getName(), p.getDone(), p.getDoneValue(), p.getDoneDelay(), table, moveTimeout); + } + else if(p.getType().equals("Double")){ + a = new ChannelAccessTableActuator(p.getName(), p.getDone(), Double.parseDouble(p.getDoneValue()), p.getDoneDelay(), table, moveTimeout); + } + else{ + // Default + a = new ChannelAccessTableActuator(p.getName(), p.getDone(), Integer.parseInt(p.getDoneValue()), p.getDoneDelay(), table, moveTimeout); + } + + a.setAsynchronous(p.isAsynchronous()); + Actor actuator = a; + + aLoop.getActors().add(actuator); + + // Add a sensor for the readback + String name = ap.getReadback(); + if(name==null){ + name = ap.getName(); + } + ChannelAccessDoubleSensor sensor = new ChannelAccessDoubleSensor(ap.getId(), name); + aLoop.getSensors().add(sensor); + } + else if (p instanceof RegionPositioner){ + RegionPositioner rp = (RegionPositioner) p; + + ComplexActuator actuator = new ComplexActuator(); + /* + * Regions are translated into a complex actor consisting of a LinearActuator + * If consecutive regions are overlapping, i.e. end point of region a equals the + * start point of region b then the start point for the LinearActuator of region b + * is changes to its next step (start+/-stepSize depending on whether end position of the + * region is > or < start of the region) + */ + Region lastRegion = null; + for(Region r: rp.getRegion()){ + // Normal region + if(r.getFunction()==null){ + + // Check whether regions are consecutive + double start = r.getStart(); + if(lastRegion!=null && start == lastRegion.getEnd()){ // TODO verify whether double comparison is ok + if(r.getStart() act; + if(rp.getType().equals("String")){ + act = new ChannelAccessLinearActuator(rp.getName(), rp.getDone(), rp.getDoneValue(), rp.getDoneDelay(), start, r.getEnd(), r.getStepSize(), moveTimeout); + } + else if(rp.getType().equals("Double")){ + act = new ChannelAccessLinearActuator(rp.getName(), rp.getDone(), Double.parseDouble(rp.getDoneValue()), rp.getDoneDelay(), start, r.getEnd(), r.getStepSize(), moveTimeout); + } + else{ + // Default + act = new ChannelAccessLinearActuator(rp.getName(), rp.getDone(), Integer.parseInt(rp.getDoneValue()), rp.getDoneDelay(), start, r.getEnd(), r.getStepSize(), moveTimeout); + } + + act.setAsynchronous(rp.isAsynchronous()); + Actor a = act; + + ComplexActuator ca = new ComplexActuator(); + ca.getActors().add(a); + ca.getPreActions().addAll(mapActions(r.getPreAction())); + actuator.getActors().add(ca); + lastRegion = r; + } + else{ + // Function based region + + // Cannot check whether the regions are consecutive as the function + // used might change the start value to something else + // [THIS LIMITATION NEEDS TO BE SOMEHOW RESOLVED IN THE NEXT VERSIONS] + JythonFunction function = mapFunction(r.getFunction()); + ChannelAccessFunctionActuator act; + if(rp.getType().equals("String")){ + act = new ChannelAccessFunctionActuator(rp.getName(), rp.getDone(), rp.getDoneValue(), rp.getDoneDelay(), function, r.getStart(), r.getEnd(), r.getStepSize(), moveTimeout); + } + else if(rp.getType().equals("Double")){ + act = new ChannelAccessFunctionActuator(rp.getName(), rp.getDone(), Double.parseDouble(rp.getDoneValue()), rp.getDoneDelay(), function, r.getStart(), r.getEnd(), r.getStepSize(), moveTimeout); + } + else{ + // Default + act = new ChannelAccessFunctionActuator(rp.getName(), rp.getDone(), Integer.parseInt(rp.getDoneValue()), rp.getDoneDelay(), function, r.getStart(), r.getEnd(), r.getStepSize(), moveTimeout); + } + + act.setAsynchronous(rp.isAsynchronous()); + Actor a = act; + + ComplexActuator ca = new ComplexActuator(); + ca.getActors().add(a); + ca.getPreActions().addAll(mapActions(r.getPreAction())); + actuator.getActors().add(ca); + lastRegion = r; + } + } + aLoop.getActors().add(actuator); + + // Add a sensor for the readback + String name = rp.getReadback(); + if(name==null){ + name = rp.getName(); + } + ChannelAccessDoubleSensor sensor = new ChannelAccessDoubleSensor(rp.getId(), name); + aLoop.getSensors().add(sensor); + } + else if(p instanceof PseudoPositioner){ + PseudoPositioner pp =(PseudoPositioner) p; + PseudoActuatorSensor actorSensor = new PseudoActuatorSensor(pp.getId(), pp.getCounts()); + + // Register as actor + aLoop.getActors().add(actorSensor); + + // Register as sensor + aLoop.getSensors().add(actorSensor); + } + else{ + // Not supported + logger.warning("Mapping for "+p.getClass().getName()+" not available"); + } + } + + // Translate settling time to post actor action + // Only add the post actor action if the settling time is > 0 + if(stime>0){ + Double delay = stime*1000; + aLoop.getPostActorActions().add(new Delay(delay.longValue())); + } + + // Map actions between positioner and detector + aLoop.getPreSensorActions().addAll(mapActions(dimension.getAction())); + + + // Map guard (if specified) + Guard g = dimension.getGuard(); + if(g != null){ + // Map conditions + List conditions = new ArrayList(); + for(GuardCondition con: g.getCondition()){ + Object value = null; + if(con.getType().equals("Integer")){ + value = new Integer(con.getValue()); + } + else if(con.getType().equals("Double")){ + value = new Double(con.getValue()); + } + else{ + value = con.getValue(); + } + + conditions.add(new ChannelAccessGuardCondition(con.getChannel(), value)); + } + // Create guard and add to loop + ChannelAccessGuard guard = new ChannelAccessGuard(conditions); + aLoop.setGuard(guard); + } + + // Map detectors + for(Detector detector : dimension.getDetector()){ + mapDetector(aLoop, detector); + } + + + // Mapping dimension post-actions + aLoop.getPostActions().addAll(mapActions(dimension.getPostAction())); + + + return aLoop; + } + + /** + * Map function + * @param f Function object in the model + * @return Internal function object + */ + private JythonFunction mapFunction(Function f){ + HashMap map = new HashMap(); + JythonGlobalVariableDictionary dict = JythonGlobalVariableDictionary.getInstance(); + for(ParameterMapping m: f.getMapping()){ + if(m instanceof VariableParameterMapping){ + VariableParameterMapping vp = (VariableParameterMapping)m; + Variable v = (Variable)vp.getName(); + JythonGlobalVariable var = dict.getVariable(v.getName()); + var.setValue(v.getValue()); + map.put(vp.getVariable(), var); + } + } + JythonFunction function = new JythonFunction(f.getScript(), map); + return function; + } + + private void mapDetector(ActorSensorLoop aLoop, Detector detector){ + if(detector instanceof ScalarDetector){ + ScalarDetector sd = (ScalarDetector) detector; + + // Add pre actions + aLoop.getPreSensorActions().addAll(mapActions(sd.getPreAction())); + + // Add sensor + Sensor sensor; + if(sd.getType().equals("String")){ + sensor = new ChannelAccessStringSensor(sd.getId(), sd.getName()); + } + else{ + sensor = new ChannelAccessDoubleSensor(sd.getId(), sd.getName()); + } + + aLoop.getSensors().add(sensor); + } + else if (detector instanceof ArrayDetector){ + ArrayDetector ad = (ArrayDetector) detector; + + // Add pre actions + aLoop.getPreSensorActions().addAll(mapActions(ad.getPreAction())); + + // Ad sensor + ChannelAccessDoubleArraySensor sensor = new ChannelAccessDoubleArraySensor(ad.getId(), ad.getName(), ad.getArraySize()); + aLoop.getSensors().add(sensor); + } + else if (detector instanceof DetectorOfDetectors){ + DetectorOfDetectors dd = (DetectorOfDetectors) detector; + + // Add pre actions + aLoop.getPreSensorActions().addAll(mapActions(dd.getPreAction())); + + for(Detector d: dd.getDetector()){ + // Recursively call mapping method + mapDetector(aLoop, d); + } + } + else if (detector instanceof Timestamp){ + Timestamp dd = (Timestamp) detector; + + // Ad sensor + MillisecondTimestampSensor sensor = new MillisecondTimestampSensor(dd.getId()); + aLoop.getSensors().add(sensor); + } + else{ + // Not supported + logger.warning("Detector type "+detector.getClass().getName()+" not supported"); + } + } + + /** + * Map OTF dimension onto a OTF loop + * @param dimension + * @return + */ + private ActionLoop mapContinuousDimension(ContinuousDimension dimension){ + + ActionLoop aLoop = null; + + if(!configuration.isOtfUseCrlogic()){ + + // USE OTFLOGIC FOR CONTINUOUS "SCANS" + + // Create loop + boolean zigZag = dimension.isZigzag(); // default value is false + + OTFLoop actionLoop = new OTFLoop(configuration.getOtfChannelPrefix(), configuration.getOtfNfsServer(), configuration.getOtfNfsShare(), configuration.getOtfSmbShare(), zigZag); + + actionLoop.getPreActions().addAll(mapActions(dimension.getPreAction())); + + // Map positioner + ContinuousPositioner p = dimension.getPositioner(); + double backlash = 0; + if(p.getAdditionalBacklash()!=null){ + backlash = p.getAdditionalBacklash(); + } + OTFActuator actor = new OTFActuator(p.getId(), p.getName(), p.getReadback(), p.getStart(), p.getEnd(), p.getStepSize(), p.getIntegrationTime(), backlash); + actionLoop.setActor(actor); + + actionLoop.getSensors().add(new OTFReadbackSensor(p.getId())); + + // Map sensors + // ATTENTION: the sequence of the mapping depends on the sequence in the schema file ! + int cnt = 0; + for(SimpleScalarDetector detector: dimension.getDetector()){ + if(cnt<8){ // Only up to 8 additional channels supported + actionLoop.getSensors().add(new OTFNamedChannelSensor(detector.getId(), detector.getName())); + } + cnt++; + } + + cnt = 0; + for(ScalerChannel detector: dimension.getScaler()){ + if(cnt<16){ // Only up to 16 scaler channels supported + actionLoop.getSensors().add(new OTFScalerChannelSensor(detector.getId(), detector.getChannel())); + } + cnt++; + } + + Timestamp detector = dimension.getTimestamp(); + if(detector != null){ + actionLoop.getSensors().add(new MillisecondTimestampSensor(detector.getId())); + } + + actionLoop.getPostActions().addAll(mapActions(dimension.getPostAction())); + + aLoop = actionLoop; + } + else{ + // USE CRLOGIC FOR CONTINUOUS "SCANS" + + boolean hcrOnly = true; + for(SimpleScalarDetector detector: dimension.getDetector()){ + if(detector.isScr()){ + hcrOnly=false; + break; + } + } + + + // BEGIN configure HCRLOGIC + + // Create loop + boolean zigZag = dimension.isZigzag(); // default value is false + + CrlogicLoop actionLoop = new CrlogicLoop(configuration.getOtfCrlogicPrefix(), configuration.getOtfNfsServer(), configuration.getOtfNfsShare(), configuration.getOtfSmbShare(), zigZag); + + actionLoop.getPreActions().addAll(mapActions(dimension.getPreAction())); + + // Map positioner + ContinuousPositioner p = dimension.getPositioner(); + double backlash = 0; + if(p.getAdditionalBacklash()!=null){ + backlash = p.getAdditionalBacklash(); + } + OTFActuator actor = new OTFActuator(p.getId(), p.getName(), p.getReadback(), p.getStart(), p.getEnd(), p.getStepSize(), p.getIntegrationTime(), backlash); + actionLoop.setActor(actor); + + // Map sensors + // ATTENTION: the sequence of the mapping depends on the sequence in the schema file ! + for(SimpleScalarDetector detector: dimension.getDetector()){ + if(!detector.isScr()){ + actionLoop.getSensors().add(new OTFNamedChannelSensor(detector.getId(), detector.getName())); + } + } + + for(ScalerChannel detector: dimension.getScaler()){ + actionLoop.getSensors().add(new OTFScalerChannelSensor(detector.getId(), detector.getChannel())); + } + + Timestamp tdetector = dimension.getTimestamp(); + if(tdetector != null){ + actionLoop.getSensors().add(new MillisecondTimestampSensor(tdetector.getId())); + } + + actionLoop.getPostActions().addAll(mapActions(dimension.getPostAction())); + + // END Configure HCRLOGIC + + + if(hcrOnly){ + // There are no additional channels to be read out while taking data via hcrlogic + // Therefor we just register the hcr loop as action loop + + aLoop = actionLoop; + } + else{ + List sensors = new ArrayList(); + for(SimpleScalarDetector detector: dimension.getDetector()){ + if(detector.isScr()){ + sensors.add(new ChannelAccessDoubleSensor(detector.getId(), detector.getName())); + } + } + // Create soft(ware) based crlogic + ScrlogicLoop scrlogic = new ScrlogicLoop(sensors); + + // Create parallel logic + ParallelCrlogic pcrlogic = new ParallelCrlogic(actionLoop, scrlogic); + + aLoop = pcrlogic; + } + + } + + return aLoop; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/aq/AcquisitionEngineConfiguration.java b/ch.psi.fda/src/main/java/ch/psi/fda/aq/AcquisitionEngineConfiguration.java new file mode 100644 index 0000000..0c8accd --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/aq/AcquisitionEngineConfiguration.java @@ -0,0 +1,233 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.aq; + +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import ch.psi.fda.install.ApplicationConfigurator; + +/** + * @author ebner + * + */ +public class AcquisitionEngineConfiguration { + + private static final AcquisitionEngineConfiguration instance = new AcquisitionEngineConfiguration(); + + private String otfChannelPrefix = ""; + private String otfNfsServer = ""; + private String otfNfsShare = ""; + private String otfSmbShare = ""; + private String otfScalerPrefix = ""; + private boolean otfUseCrlogic = false; + private String otfCrlogicPrefix = ""; + + /** + * Base directory for data. The directory may contain date macros. The string may contain any @see java.text.SimpleDateFormat + * patterns within ${ } brackets. The macros are resolved with the actual time while the get method + * of this property is called. + */ + private String dataBaseDirectory = System.getProperty("user.home"); + /** + * Prefix of the data file. The prefix may contain date macros. The string may contain any @see java.text.SimpleDateFormat + * patterns within ${ } brackets. The macros are resolved with the actual time while the get method + * of this property is called. + */ + private String dataFilePrefix = ""; + + /** + * Maximum time for a actor move + */ + private Long actorMoveTimeout = 600000l; // 10 Minutes maximum move time + + private String smptServer; + + /** + * Default Constructor + * The constructor will read the configuration from the /fda.properties file (resource) located in the classpath. + */ + private AcquisitionEngineConfiguration(){ + loadConfiguration(); + } + + /** + * Get instance of this configuration + * @return configuration + */ + public static AcquisitionEngineConfiguration getInstance(){ + return(instance); + } + + /** + * Load configuration from properties file + */ + private void loadConfiguration() { + String config = System.getProperty(ApplicationConfigurator.FDA_CONFIG_FILE_ARGUMENT); + + if(config == null){ + throw new RuntimeException("No configuration file specified via -D"+ApplicationConfigurator.FDA_CONFIG_FILE_ARGUMENT+"=..."); + } + + Properties properties = new Properties(); + try { + properties.load(new FileReader(config)); + } catch (FileNotFoundException e) { + throw new RuntimeException("Configuration file "+config+" not found", e); + } catch (IOException e) { + throw new RuntimeException("Cannot read configuration file "+config, e); + } + + + + otfChannelPrefix = properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".otf.channelPrefix", ""); + otfScalerPrefix = properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".otf.scalerPrefix", ""); + otfNfsServer = properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".otf.nfsServer", ""); + otfNfsShare = properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".otf.nfsShare", ""); + otfSmbShare = properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".otf.smbShare", ""); + otfUseCrlogic = new Boolean(properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".otf.useCrlogic", "false")); + otfCrlogicPrefix = properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".otf.crlogicPrefix", ""); + + // Workaround +// dataBaseDirectory = properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".data.baseDirectory", "."); + dataBaseDirectory = System.getProperty(ApplicationConfigurator.FDA_HOME_ARGUMENT)+"/data"; + + dataFilePrefix = properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".data.filePrefix",""); + + actorMoveTimeout = new Long(properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".actorMoveTimeout","600000")); + + smptServer= properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".notification.host","mail.psi.ch"); + + } + + /** + * Reload this configuration from the corresponding properties file + * @throws IOException + */ + public void reloadConfiguration() throws IOException{ + loadConfiguration(); + } + + /** + * @return the otfChannelPrefix + */ + public String getOtfChannelPrefix() { + return otfChannelPrefix; + } + + /** + * @return the otfScalerPrefix + */ + public String getOtfScalerPrefix() { + return otfScalerPrefix; + } + + /** + * @return the otfNfsServer + */ + public String getOtfNfsServer() { + return otfNfsServer; + } + + /** + * @return the otfNfsShare + */ + public String getOtfNfsShare() { + return otfNfsShare; + } + + /** + * @return the otfSmbShare + */ + public String getOtfSmbShare() { + return otfSmbShare; + } + + /** + * @return the otfUseCrlogic + */ + public boolean isOtfUseCrlogic() { + return otfUseCrlogic; + } + + /** + * @return the dataBaseDirectory + */ + public String getDataBaseDirectory() { + return dataBaseDirectory; + } + + /** + * @return the dataFilePrefix + */ + public String getDataFilePrefix() { + return dataFilePrefix; + } + + /** + * @return the smptServer + */ + public String getSmptServer() { + return smptServer; + } + + + /** + * @return the otfCrlogicPrefix + */ + public String getOtfCrlogicPrefix() { + return otfCrlogicPrefix; + } + + /** + * @return the actorMoveTimeout + */ + public Long getActorMoveTimeout() { + return actorMoveTimeout; + } + + public String replaceMacros(String string, Date date, String name){ + String newString = string; + + // Replace scan name macros + newString = newString.replaceAll("\\$\\{name\\}", name); + + + // Replace date macros + Pattern pattern = Pattern.compile("\\$\\{[a-z,A-Z,-,_,:]*\\}"); + Matcher matcher = pattern.matcher(newString); + while(matcher.find()){ + String datePattern = matcher.group(); + datePattern = datePattern.replaceAll("\\$\\{", ""); + datePattern = datePattern.replaceAll("\\}", ""); + SimpleDateFormat datef = new SimpleDateFormat(datePattern); + newString = matcher.replaceFirst(datef.format(date)); + matcher = pattern.matcher(newString); + } + return newString; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/aq/AcquisitionMain.java b/ch.psi.fda/src/main/java/ch/psi/fda/aq/AcquisitionMain.java new file mode 100644 index 0000000..534e455 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/aq/AcquisitionMain.java @@ -0,0 +1,432 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.aq; + +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.ScrollPaneLayout; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.GnuParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.OptionBuilder; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +import sun.misc.Signal; +import sun.misc.SignalHandler; + +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.gui.ProgressPanel; +import ch.psi.fda.gui.ScrollableFlowPanel; +import ch.psi.fda.install.ApplicationConfigurator; +import ch.psi.fda.model.ModelManager; +import ch.psi.fda.model.v1.Configuration; +import ch.psi.fda.model.v1.Data; +import ch.psi.fda.visualizer.Visualizer; + +/** + * Main class for data acquisition + * @author ebner + * + */ +public class AcquisitionMain { + + + // Get Logger + private static Logger logger = Logger.getLogger(AcquisitionMain.class.getName()); + + /** + * Main Program + * Returns -1 if wrong number of arguments are passed + * + * @param args Arguments of the program + */ + public static void main(String[] args) { + + String scriptname = "fda_scan"; + + Integer iterations = null; + boolean autoclose = false; + boolean nogui = false; + String files[] = null; + + HashMap varTable = new HashMap(); + + // Iterations option + OptionBuilder.hasArg(); + OptionBuilder.withArgName("iterations"); + OptionBuilder.withDescription("Number of iterations"); + OptionBuilder.withType(new Integer(1)); + Option o_iterations = OptionBuilder.create( "iterations"); + + // Variables option + OptionBuilder.hasArg(); + OptionBuilder.withArgName("variables"); + OptionBuilder.withDescription("Scan variables - variables are specified in the form var=value,var2=value2"); + OptionBuilder.withType(new Integer(1)); + Option o_variables = OptionBuilder.create( "variables"); + + Option o_autoclose = new Option( "autoclose", "Close down application after scan" ); + Option o_init = new Option( "initialize", "Initialize application directories and configuration files" ); + Option o_nogui = new Option( "nogui", "Do not show scan GUI" ); + + Options options = new Options(); + options.addOption(o_variables); + options.addOption(o_iterations); + options.addOption(o_autoclose); + options.addOption(o_init); + options.addOption(o_nogui); + + CommandLineParser parser = new GnuParser(); + // Parse the command line arguments + try { + CommandLine line = parser.parse( options, args ); + + // Initialize application + if( line.hasOption(o_init.getOpt()) ){ + // Initialize application + ApplicationConfigurator ac = new ApplicationConfigurator(); + ac.initializeApplication(); + System.exit(0); + } + + // Check whether exactly one file is specified +// if(line.getArgs().length>1){ +// throw new ParseException("Only up to one argument is supported"); +// } + if(line.getArgs().length<1){ + throw new ParseException("One argument is required"); + } + + files=line.getArgs(); + + // Iterations option + if( line.hasOption(o_iterations.getOpt()) ){ + iterations = Integer.parseInt(line.getOptionValue(o_iterations.getOpt())); + } + + // Variables + if( line.hasOption(o_variables.getOpt()) ){ + String variables = line.getOptionValue(o_variables.getOpt() ); + String[] vars = variables.split(","); + for(String varp:vars){ + String[] pair = varp.split("="); + if(pair.length!=2){ + throw new ParseException("Variables are not specified the correct way. -variables var1=val1,var2=val2"); + } + varTable.put(pair[0], pair[1]); + } + } + + // Autoclose option + if( line.hasOption( o_autoclose.getOpt() ) ) { + autoclose = true; + } + + // No GUI option + if( line.hasOption( o_nogui.getOpt() ) ) { + nogui = true; + } + + } catch (ParseException e) { + System.err.println(e.getMessage()); + HelpFormatter formatter = new HelpFormatter(); + formatter.printUsage(new PrintWriter(System.out, true), HelpFormatter.DEFAULT_WIDTH, scriptname, options); + System.exit(-1); + } + + // Run application + try{ + for(String file: files){ + run(new File(file), iterations, autoclose, nogui, varTable); + } + + // Close application automatically if autoclose option is set (and visualizations are specified) + if(nogui || autoclose ){ + System.exit(0); + } + + } + catch(Exception ee){ + System.out.println("Acquisition failed due to: "+ee.getMessage()); + logger.log(Level.SEVERE, "Acquisition failed due to: ", ee); // Do not print stack trace + System.exit(-1); + } + + } + + /** + * Run scan + * @param file Scan file + * @param iterations Number of iterations + * @param autoclose Flag whether to close the application automatically after the scan + * @param nogui Flag whether to run the scan with a GUI + * @param variables Table of scan variables + */ + private static void run(File file, Integer iterations, boolean autoclose, boolean nogui, HashMap variables){ + + // Initialize application + ApplicationConfigurator ac = new ApplicationConfigurator(); + ac.initializeApplication(); + + // Read in base configuration +// AcquisitionEngineConfiguration configuration = AcquisitionEngineConfiguration.getInstance(); + + if(!file.exists()){ + throw new RuntimeException("File "+file.getAbsolutePath()+" does not exist"); + } + + Configuration c; + try { + if(file.getName().endsWith(".xsl")){ + c = ModelManager.unmarshall(file, variables); + } + else{ + c = ModelManager.unmarshall(file); + } + + } catch (Exception e) { + throw new RuntimeException("Unable to deserialize configuration: "+e.getMessage(), e); + } + + // Set data file name + // Determine name used for the data file + String name = file.getName(); + name = name.replaceAll("\\.xml$", ""); + + if(c.getData()!=null){ + Data data = c.getData(); + // Only update filename if no name is specified in xml file + if(data.getFileName()==null){ + data.setFileName(name); + } + } + else{ + Data data = new Data(); + data.setFileName(name); + c.setData(data); + } + + + // Override number of executions + if(iterations != null){ + c.setNumberOfExecution(iterations); + } + // Fix configuration if iterations is specified with 0 and no iterations option is specified + if(c.getNumberOfExecution()==0){ + c.setNumberOfExecution(1); + } + + // Create/get acquisition engine + final Acquisition acquisition = new Acquisition(); + + boolean vis = false; + // Only register data visualization task/processor if there are visualizations + if(c.getVisualization().size()>0 && !nogui){ + vis=true; + } + + DataQueue vdq = acquisition.initalize(c, vis); + + Visualizer visualizer = null; + // Only register data visualization task/processor if there are visualizations + if(vis){ + visualizer = new Visualizer(vdq, c.getVisualization()); + // If there is a continous dimension only update plot at the end of a line + if(c.getScan() != null && c.getScan().getCdimension()!=null){ + visualizer.setUpdateAtStreamElement(false); + visualizer.setUpdateAtStreamDelimiter(true); + visualizer.setUpdateAtEndOfStream(true); + } + } + + // GUI GUI GUI GUI GUI GUI GUI + ProgressPanel progressPanel = null; + if(visualizer != null){ // Only bring up GUI if there are some plots ... + // Visualizations + JPanel opanel = new ScrollableFlowPanel(); + opanel.setLayout(new FlowLayout()); + + JScrollPane spane = new JScrollPane(opanel, ScrollPaneLayout.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneLayout.HORIZONTAL_SCROLLBAR_NEVER); + JTabbedPane tpane = new JTabbedPane(); + tpane.addTab("Overview", spane); + + for(JPanel p: visualizer.getPlotPanels()){ + opanel.add(p); + } + + final JFrame frame = new JFrame("FDA: "+acquisition.getDatafileName()); + frame.setSize(1200,800); + + // Create progress panel + progressPanel = new ProgressPanel(); + progressPanel.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + try { + acquisition.abort(); + } catch (Exception e1) { + logger.log(Level.SEVERE, "Exception occured while aborting scan", e1); + } + + } + }); + + + JSplitPane splitPane = new JSplitPane(); + splitPane.setLeftComponent(progressPanel); + splitPane.setRightComponent(tpane); + + frame.add(splitPane); +// frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + frame.addWindowListener(new WindowAdapter(){ + @Override + public void windowClosing(WindowEvent we){ + if(acquisition.isActive()){ + // Abort acquisition + acquisition.abort(); + } + + // Wait until acquisition is aborted. Maximum wait 10*100milliseconds before forcefully + // terminate application + int count=0; + while(acquisition.isActive()){ + if(count == 10){ + break; + } + + // Sleep 100 milliseconds + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + count++; + } + + // Terminate program + System.exit(0); + } + }); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);//.DO_NOTHING_ON_CLOSE); +// frame.setVisible(true); + + java.awt.EventQueue.invokeLater(new Runnable() { + public void run() { + frame.setVisible(true); + } + }); + } + // GUI GUI GUI GUI GUI GUI GUI + + // CLI CLI CLI CLI + // Register the scan engine as Signal Handler + Signal.handle(new Signal("INT"), new SignalHandler() { + /** + * Thread save signal counter + */ + private AtomicInteger signalCount= new AtomicInteger(0); + + /** + * Testing signal handler (in Eclipse) use this after starting scan: + * + * SL5: A=`ps -ef | tail -10 | grep jav[a] | awk '{printf $2}'`;kill -2 $A + * MacOS X: A=`ps -ef | grep AcquisitionMai[n] | awk '{printf $2}'`;kill -2 $A + * + * on the command line use CTRL-C + */ + @Override + public void handle(Signal signal) { + + logger.finest("Received signal: "+signal); + + int count = signalCount.incrementAndGet(); + + // If signal is received more than 1 time forcefully abort application + if(count>1){ + logger.info("Terminate application"); + System.exit(2); + } + + // Abort acquisition engine + if(acquisition.isActive()){ + // Abort acquisition + acquisition.abort(); + } + } + }); + // CLI CLI CLI CLI + + + // Run acquisition engine + try { + if(visualizer != null){ + // Start visualization + visualizer.startVisualization(); + } + + acquisition.execute(); + + if(visualizer != null){ + // Stop visualization + visualizer.stopVisualization(); + } + + } catch (InterruptedException e1) { + throw new RuntimeException("Acquisition was interrupted",e1); + } + finally{ + acquisition.destroy(); + } + + + // GUI GUI GUI GUI GUI GUI GUI + // Set progress panel to done + if(progressPanel != null){ + // Can this be done via a Listener? + progressPanel.done(); + } + // GUI GUI GUI GUI GUI GUI GUI + + } + + + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/co/ConversionEngine.java b/ch.psi.fda/src/main/java/ch/psi/fda/co/ConversionEngine.java new file mode 100644 index 0000000..c60a314 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/co/ConversionEngine.java @@ -0,0 +1,257 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.co; + +import java.io.File; +import java.io.PrintWriter; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.GnuParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.OptionBuilder; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +import ch.psi.fda.deserializer.DataDeserializer; +import ch.psi.fda.deserializer.DataDeserializerMDA; +import ch.psi.fda.deserializer.DataDeserializerTXT; +import ch.psi.fda.serializer.DataSerializer; +import ch.psi.fda.serializer.DataSerializerMAT; +import ch.psi.fda.serializer.DataSerializerMAT2D; +import ch.psi.fda.serializer.DataSerializerMAT2DZigZag; +import ch.psi.fda.serializer.DataSerializerMDA; +import ch.psi.fda.serializer.DataSerializerTXT; +import ch.psi.fda.serializer.DataSerializerTXT2D; +import ch.psi.fda.serializer.DataSerializerTXTSplit; + +/** + * Visualize data according to the scan description + * + * @author ebner + * + */ +public class ConversionEngine { + + // Get Logger + private static Logger logger = Logger.getLogger(ConversionEngine.class.getName()); + + public enum Writer {TXT, TXT_2D, TXT_SPLIT, MAT, MAT_2D, MDA, MAT_2D_Z}; + public enum Reader {TXT, MDA}; + + /** + * Default constructor + */ + public ConversionEngine(){ + } + + /** + * Visualize data + * @param input + * @param output + * @param writer Type of writer to use + * @throws InterruptedException + */ + public void convert(File input, File output, Reader reader, Writer writer) throws InterruptedException{ + + // Check existence input file + if(input==null){ + throw new IllegalArgumentException("Input file not specified (is null)"); + } + else if(!input.exists()){ + throw new IllegalArgumentException("Input file ["+input.getAbsolutePath()+"] does not exist"); + } + + // Check existence output file + if(output==null){ + throw new IllegalArgumentException("Output file not specified (is null)"); + } + else if(output.exists()){ + throw new IllegalArgumentException("Output file ["+output.getAbsolutePath()+"] already exists"); + } + + // Create deserializer + DataDeserializer deserializer; + if(reader.equals(Reader.TXT)){ + deserializer = new DataDeserializerTXT(input); + } + else if(reader.equals(Reader.MDA)){ + deserializer = new DataDeserializerMDA(input); + } + else{ + throw new IllegalArgumentException("Reader of type "+reader+" not supported."); + } + + DataSerializer serializer; + if(writer.equals(Writer.MAT)){ + serializer = new DataSerializerMAT(deserializer.getQueue(), output); + } + else if(writer.equals(Writer.MAT_2D)){ + serializer = new DataSerializerMAT2D(deserializer.getQueue(), output); + } + else if(writer.equals(Writer.TXT)){ + serializer = new DataSerializerTXT(deserializer.getQueue(), output, false); + } + else if(writer.equals(Writer.TXT_2D)){ + serializer = new DataSerializerTXT2D(deserializer.getQueue(), output); + } + else if(writer.equals(Writer.TXT_SPLIT)){ + serializer = new DataSerializerTXTSplit(deserializer.getQueue(), output); + } + else if(writer.equals(Writer.MDA)){ + serializer = new DataSerializerMDA(deserializer.getQueue(), output); + } + else if(writer.equals(Writer.MAT_2D_Z)){ + serializer = new DataSerializerMAT2DZigZag(deserializer.getQueue(), output); + } + else{ + throw new IllegalArgumentException("Writer of type "+writer+" not supported."); + } + + // Start deserializer and serializer + Thread td = new Thread(deserializer); + Thread ts = new Thread(serializer); + + td.start(); + ts.start(); + + td.join(); + ts.join(); + } + + /** + * This method accepts a data file and an option (-file) specifying the scan configuration file + * used for creating the data file. If no option is specified the configuration file is derived by the + * data file name. + * + * @param args Command line arguments + */ + public static void main(String[] args) { + String scriptname = "fda_convert"; + + File outfile = null; + File infile = null; + Reader reader = Reader.TXT; + Writer writer = Writer.TXT; + + + OptionBuilder.hasArg(); + OptionBuilder.withArgName("writer"); + OptionBuilder.withDescription("Output format"); + OptionBuilder.withType(new String()); + Option o_writer = OptionBuilder.create( "writer"); + + OptionBuilder.hasArg(); + OptionBuilder.withArgName("reader"); + OptionBuilder.withDescription("Input format"); + OptionBuilder.withType(new String()); + Option o_reader = OptionBuilder.create( "reader"); + + Options options = new Options(); + options.addOption(o_writer); + options.addOption(o_reader); + + CommandLineParser parser = new GnuParser(); + // Parse the command line arguments + try { + CommandLine line = parser.parse( options, args ); + + // Check whether exactly one file is specified + if(line.getArgs().length != 2){ + throw new ParseException("Two arguments required"); + } + + infile = new File(line.getArgs()[0]); + outfile= new File(line.getArgs()[1]); + + if( line.hasOption(o_reader.getOpt() )){ + String wr = line.getOptionValue(o_reader.getOpt()); + try{ + Reader r = Reader.valueOf(wr.toUpperCase()); + reader = r; + } + catch(IllegalArgumentException e){ + throw new ParseException("Data type "+ wr + " not supported"); + } + } + else{ + String suffix = infile.getName().replaceAll("^.*\\.", ""); + try{ + Reader r = Reader.valueOf(suffix.toUpperCase()); + reader = r; + } + catch(IllegalArgumentException e){ + throw new ParseException("Data type "+ suffix + " not supported"); + } + } + + + if( line.hasOption(o_writer.getOpt() )){ + String wr = line.getOptionValue(o_writer.getOpt()); + try{ + Writer v = Writer.valueOf(wr.toUpperCase()); + writer = v; + } + catch(IllegalArgumentException e){ + throw new ParseException("Data type "+ wr + " not supported"); + } + } + else{ + String suffix = outfile.getName().replaceAll("^.*\\.", ""); + try{ + Writer v = Writer.valueOf(suffix.toUpperCase()); + writer = v; + } + catch(IllegalArgumentException e){ + throw new ParseException("Data type "+ suffix + " not supported"); + } + } + + // Debug output + logger.finest("Using writer: "+writer); + + } catch (ParseException e) { + System.err.println(e.getMessage()); + HelpFormatter formatter = new HelpFormatter(); + formatter.printUsage(new PrintWriter(System.out, true), HelpFormatter.DEFAULT_WIDTH, scriptname, options); + StringBuffer b = new StringBuffer(); + for(Writer w: Writer.values()){ + b.append(" "); + b.append(w.toString().toLowerCase()); + } + System.out.println("Supported writer(s):"+ b); + System.exit(-1); + } + + ConversionEngine e = new ConversionEngine(); + try{ + e.convert(infile, outfile, reader, writer); + } + catch(Exception ee){ + System.out.println("Conversion failed due to: "+ee.getMessage()); + logger.log(Level.WARNING, "Conversion failed due to Exception:", ee); + System.exit(-1); + } + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/Action.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/Action.java new file mode 100644 index 0000000..c91f1de --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/Action.java @@ -0,0 +1,46 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core; + +/** + * @author ebner + * + */ +public interface Action { + + /** + * Execute logic of the action + */ + public void execute() throws InterruptedException; + + /** + * Abort the execution logic of the action + */ + public void abort(); + + /** + * Destroy action. + * Can be used for the cleanup of used resources of the action (e.g. to close connections, ...) if + * this cannot be done automatically by the GarbageCollector. + * + * After calling this method the action must not be executed any more! + */ + public void destroy(); +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/ActionLoop.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/ActionLoop.java new file mode 100644 index 0000000..0e50c67 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/ActionLoop.java @@ -0,0 +1,69 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core; + +import java.util.List; + +import ch.psi.fda.core.messages.DataQueue; + +/** + * Loop of actions to accomplish a task. Depending on the loop + * actions may be executed in a different way. + * + * @author ebner + * + */ +public interface ActionLoop extends Action { + + /** + * Prepare ActionLoop for execution. + */ + public void prepare(); + + /** + * Cleanup resources used by this ActionLoop while it was executed. + */ + public void cleanup(); + + /** + * Get the pre actions of the Loop + * @return pre actions + */ + public List getPreActions(); + + /** + * Get the post actions of the loop + * @return post actions + */ + public List getPostActions(); + + /** + * @return is a datagroup + */ + public boolean isDataGroup(); + + /** + * Set whether data of the loop belongs to a own data group + * @param dataGroup + */ + public void setDataGroup(boolean dataGroup); + + public DataQueue getDataQueue(); +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/Actor.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/Actor.java new file mode 100644 index 0000000..37a3829 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/Actor.java @@ -0,0 +1,64 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core; + +/** + * + * @author ebner + * + */ +public interface Actor { + /** + * Set actor value + */ + public void set() throws InterruptedException; + + /** + * Function to check whether the actor has a next set value + * @return Returns true if there is an actor value for the next iteration. + * False if there is no actor value (i.e. this will be the last iteration in the ActionLoop) + */ + public boolean hasNext(); + + /** + * Initialize the actor to the start + */ + public void init(); + + /** + * Reverse the set values of the actor + */ + public void reverse(); + + /** + * Reset the actuator to its initial configuration + */ + public void reset(); + + + /** + * Destroy action. + * Can be used for the cleanup of used resources of the actor (e.g. to close connections, ...) if + * this cannot be done automatically by the GarbageCollector. + * + * After calling this method the actor must not be used any more! + */ + public void destroy(); +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/ActorSetCallable.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/ActorSetCallable.java new file mode 100644 index 0000000..8357123 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/ActorSetCallable.java @@ -0,0 +1,47 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core; + +import java.util.concurrent.Callable; + +/** + * Callable used for parallel execution of the set method of an actor + * @author ebner + * + */ +public class ActorSetCallable implements Callable { + + // Callable actor + private Actor actor; + + public ActorSetCallable(Actor actor){ + this.actor = actor; + } + + /* (non-Javadoc) + * @see java.util.concurrent.Callable#call() + */ + @Override + public Object call() throws Exception { + actor.set(); + return null; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/EngineConfiguration.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/EngineConfiguration.java new file mode 100644 index 0000000..f55de04 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/EngineConfiguration.java @@ -0,0 +1,87 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core; + +/** + * Singleton object for holing the core engine configuration + * + * @author ebner + */ +public class EngineConfiguration { + + /** + * Flag to indicate whether the engine should fail if there is an error while reading out a sensor. + */ + private boolean failOnSensorError = true; + + /** + * Flag to indicate whether the set value of the actor should be verified after the set operation. + * Having this flag on true will enable detecting for example that a motor hit the end switch or + * did not move correctly. + */ + private boolean checkActorSet = true; + + /** + * Singleton instance + */ + private final static EngineConfiguration instance = new EngineConfiguration(); + + + + private EngineConfiguration(){ + } + + /** + * Get singleton instance of this class + * @return Engine configuration + */ + public static EngineConfiguration getInstance(){ + return instance; + } + + /** + * @return the failOnSensorError + */ + public boolean isFailOnSensorError() { + return failOnSensorError; + } + + /** + * @param failOnSensorError the failOnSensorError to set + */ + public void setFailOnSensorError(boolean failOnSensorError) { + this.failOnSensorError = failOnSensorError; + } + + /** + * @return the checkActorSet + */ + public boolean isCheckActorSet() { + return checkActorSet; + } + + /** + * @param checkActorSet the checkActorSet to set + */ + public void setCheckActorSet(boolean checkActorSet) { + this.checkActorSet = checkActorSet; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/Guard.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/Guard.java new file mode 100644 index 0000000..a8a2008 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/Guard.java @@ -0,0 +1,53 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core; + +/** + * Guard to protect specific activities. A guard can be used to check for environment changes while a + * certain activity was executed. + * + * Example: + * An Guard can be used to check whether an injection happened while the the detector were read. + * + * @author ebner + * + */ +public interface Guard { + + /** + * Initialize guard object and its internal state. + */ + public void init(); + + /** + * Check the status of the guard. + * @return Returns true if the guard condition was not constrainted since the last init call. False otherwise. + */ + public boolean check(); + + /** + * Destroy guard. + * Can be used for the cleanup of used resources of the guard (e.g. to close connections, ...) if + * this cannot be done automatically by the GarbageCollector. + * + * After calling this method the guard must not be used any more! + */ + public void destroy(); +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/Sensor.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/Sensor.java new file mode 100644 index 0000000..381b2d6 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/Sensor.java @@ -0,0 +1,50 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core; + +/** + * The sensor interface describes an entity that can be read out like a + * simple channel or (image) detector. Depending on the sensor type the + * returned data is of a certain type. + * @author ebner + * + */ +public interface Sensor { + /** + * Readout sensor. + * @return Sensor value. The type of the returned value depends on the sensor type. + */ + public Object read() throws InterruptedException; + + /** + * Get the global id of the sensor + * @return id of sensor + */ + public String getId(); + + /** + * Destroy sensor. + * Can be used for the cleanup of used resources of the sensor (e.g. to close connections, ...) if + * this cannot be done automatically by the GarbageCollector. + * + * After calling this method the sensor must not be used any more! + */ + public void destroy(); +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/actions/ChannelAccessCondition.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/actions/ChannelAccessCondition.java new file mode 100644 index 0000000..fc8611a --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/actions/ChannelAccessCondition.java @@ -0,0 +1,127 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actions; + +import java.util.logging.Logger; + +import gov.aps.jca.CAException; +import ch.psi.fda.core.Action; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * Perform a put on the specified Channel Access channel. The put can be done synchronous or + * asynchronously. + * @author ebner + * + */ +public class ChannelAccessCondition implements Action { + + // Get Logger + private static Logger logger = Logger.getLogger(ChannelAccessCondition.class.getName()); + + /** + * Channel to set + */ + private final ChannelBean channel; + /** + * Value to wait for + */ + private final E expectedValue; + + private final Long timeout; + + /** + * Constructor + * @param channelName Name of the channel to set the value + * @param expectedValue Value to wait for + * @param timeout Timeout of the condition in milliseconds (null accepted - will take default wait timeout for channels ch.psi.jcae.ChannelBeanFactory.waitTimeout) + * + * @throws IllegalArgumentException Unable to initialize channel, + * Timeout specified is not >=0 + */ + @SuppressWarnings("unchecked") + public ChannelAccessCondition(String channelName, E expectedValue, Long timeout){ + + if(timeout !=null && timeout<=0){ + throw new IllegalArgumentException("Timeout must be > 0"); + } + + try { + this.channel = (ChannelBean) ChannelBeanFactory.getFactory().createChannelBean( (Class) expectedValue.getClass(), channelName, false); + } catch (CAException e) { + // Convert Exception into unchecked RuntimeException + throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+channelName+"]",e); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to initialize actuator channel [name:"+channelName+"]",e); + } + + this.expectedValue = expectedValue; + + if(timeout==null){ + this.timeout = channel.getWaitTimeout(); + } + else{ + this.timeout = timeout; + } + } + + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#execute() + */ + /** + * @throws InterruptedException + * @throws RuntimeException Channel value did not reach expected value (within the specified timeout period) + */ + @Override + public void execute() throws InterruptedException { + logger.finest("Checking channel "+channel.getName()+" for value "+expectedValue+" [timeout: "+timeout+"]" ); + try{ + channel.waitForValue(expectedValue, timeout); // Workaround use 10seconds default set timeout to check several times whether the channel has reached the value + } catch (CAException e) { + throw new RuntimeException("Channel [name:"+channel.getName()+"] did not reach expected value "+expectedValue+" ", e); + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#abort() + */ + @Override + public void abort() { + // This action cannot be aborted, therefore this method is not implemented + } + + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#destroy() + */ + @Override + public void destroy() { + // Destroy channel + try { + logger.finest("Destroy action channel: "+channel.getName()); + channel.destroy(); + } catch (CAException e) { + throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e); + } + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/actions/ChannelAccessPut.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/actions/ChannelAccessPut.java new file mode 100644 index 0000000..fec10df --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/actions/ChannelAccessPut.java @@ -0,0 +1,142 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actions; + +import java.util.logging.Logger; + +import gov.aps.jca.CAException; +import ch.psi.fda.core.Action; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * Perform a put on the specified Channel Access channel. The put can be done synchronous or + * asynchronously. + * @author ebner + * + */ +public class ChannelAccessPut implements Action { + + // Get Logger + private static Logger logger = Logger.getLogger(ChannelAccessPut.class.getName()); + + /** + * Channel to set + */ + private final ChannelBean channel; + /** + * Value to set + */ + private final E value; + /** + * Put mode, true = fire and forget, false = wait for response + */ + private final boolean asynchronous; + + private final Long timeout; + + /** + * Constructor + * @param channelName Name of the channel to set the value + * @param value Value to set + * @param asynchronous Flag whether to set the value synchronous (wait for response) or asynchronously (fire and forget) + * @param timeout Timeout used for set operation (time that set need to come back) + * + * @throws IllegalArgumentException Unable to initialize channel + */ + @SuppressWarnings("unchecked") + public ChannelAccessPut(String channelName, E value, boolean asynchronous, Long timeout){ + + try { + this.channel = ChannelBeanFactory.getFactory().createChannelBean((Class)value.getClass(), channelName, false); + } catch (CAException e) { + // Convert Exception into unchecked RuntimeException + throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+channelName+"]",e); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to initialize actuator channel [name:"+channelName+"]",e); + } + + this.value = value; + this.asynchronous = asynchronous; + this.timeout = timeout; + } + + /** + * Additional constructor for convenience. This constructor defaults the operation type to synchronous put. + * @param channelName Name of the channel to set the value + * @param value Value to set + * + * @throws RuntimeException Unable to initialize channel + */ + public ChannelAccessPut(String channelName, E value){ + this(channelName, value, false, null); + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#execute() + */ + /** + * @throws InterruptedException + * @throws RuntimeException Cannot set value on channel + */ + @Override + public void execute() throws InterruptedException { + logger.finest("Put to channel: "+channel.getName()+ " asynchronous: "+asynchronous); + try{ + if(asynchronous){ + channel.setValueNoWait(value); + } + else{ + if(timeout==null){ + channel.setValue(value); + } + else{ + channel.setValue(value, timeout); + } + } + } catch (CAException e) { + // Convert Exception into unchecked RuntimeException + throw new RuntimeException("Unable to set channel [name:"+channel.getName()+"] to value "+value, e); + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#abort() + */ + @Override + public void abort() { + // This action cannot be aborted, therefore this method is not implemented + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#destroy() + */ + @Override + public void destroy() { + // Destroy channel + try { + logger.finest("Destroy action channel: "+channel.getName()); + channel.destroy(); + } catch (CAException e) { + throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e); + } + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/actions/Delay.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/actions/Delay.java new file mode 100644 index 0000000..4c15f93 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/actions/Delay.java @@ -0,0 +1,74 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actions; + +import ch.psi.fda.core.Action; + +/** + * Wait a specific time until executing the next action ... + * @author ebner + * + */ +public class Delay implements Action { + + /** + * Time to wait + */ + private final long time; + + /** + * Constructor + * @param time Time to wait (in milliseconds) + */ + public Delay(long time){ + + // Check if delay time is positive and >0 + if(time<=0){ + throw new IllegalArgumentException("Wait time must be >0"); + } + + this.time = time; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#execute() + */ + @Override + public void execute() throws InterruptedException { + Thread.sleep(time); + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#abort() + */ + @Override + public void abort() { + // Not implemented because not needed + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#destroy() + */ + @Override + public void destroy() { + // Nothing to be done + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/actions/JythonAction.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/actions/JythonAction.java new file mode 100644 index 0000000..9eecdba --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/actions/JythonAction.java @@ -0,0 +1,169 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actions; + +import gov.aps.jca.CAException; + +import java.util.List; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +import ch.psi.fda.core.Action; +import ch.psi.fda.core.scripting.JythonParameterMapping; +import ch.psi.fda.core.scripting.JythonParameterMappingChannel; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * @author ebner + * + */ +public class JythonAction implements Action { + + // Get Logger + private static Logger logger = Logger.getLogger(JythonAction.class.getName()); + + /** + * Pattern of the entry function of the jython script + */ + private static final String entryFunction = "process"; + private static final String entryFunctionPattern = "def "+entryFunction+"\\((.*)\\):"; + + /** + * Script engine of the manipulator + */ + private ScriptEngine engine; + + /** + * Jython entry call + */ + private String jythonCall; + + public JythonAction(String script, List mapping){ + + // Workaround for Jython memory leak + // http://blog.hillbrecht.de/2009/07/11/jython-memory-leakout-of-memory-problem/ + System.setProperty("python.options.internalTablesImpl","weak"); + + // Create new script engine + this.engine = new ScriptEngineManager().getEngineByName("python"); + + // Determine script entry function and the function parameters + Pattern pattern = Pattern.compile(entryFunctionPattern); + Matcher matcher = pattern.matcher(script); + String[] functionParameters = null; + if(matcher.find() && matcher.groupCount()==1){ + logger.finest("Entry function '"+entryFunctionPattern+"' found - Identified parameters: "+matcher.group(1)); + functionParameters = matcher.group(1).split(" *, *"); + } + else{ + throw new IllegalArgumentException("Cannot determine entry function: "+entryFunctionPattern); + } + + // Check whether all function parameters have a mapping + for(int i=0;i cb; + try { + cb = ChannelBeanFactory.getFactory().createChannelBean(b.getType(), b.getChannel(), true); + } catch (CAException e) { + throw new IllegalArgumentException("Unable to establish channel: "+b.getChannel(), e); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to establish channel: "+b.getChannel(), e); + } + + // Assign channel bean to variable + engine.put(b.getVariable(), cb); + + buffer.append(b.getVariable()); + buffer.append(","); + } + buffer.setCharAt(buffer.length()-1, ')'); + + jythonCall = buffer.toString(); + } + + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#execute() + */ + @Override + public void execute() { + try { + engine.eval(jythonCall); + } catch (ScriptException e) { + throw new RuntimeException("Action failed while executing the Jython script",e); + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#abort() + */ + @Override + public void abort() { + // TODO need to find a way to abort script execution + } + + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#destroy() + */ + @Override + public void destroy() { + // Nothing to be done + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/actions/ShellAction.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/actions/ShellAction.java new file mode 100644 index 0000000..b015045 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/actions/ShellAction.java @@ -0,0 +1,102 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actions; + +import java.io.File; +import java.io.IOException; +import java.util.logging.Logger; + +import ch.psi.fda.core.Action; + +/** + * Action that executes a specified script when it is executed. + * @author ebner + * + */ +public class ShellAction implements Action{ + + // Get Logger + private static Logger logger = Logger.getLogger(ShellAction.class.getName()); + + /** + * Name (full path if it is not in the system path) of the script to execute when + * the execute() function of this action is invoked. + */ + private final String script; + + /** + * Constructor + * @param script Name of the script to execute when this action is invoked + * + * @throws IllegalArgumentException Specified script does not exist + */ + public ShellAction(String script){ + File s = new File(script); + if(!s.exists()){ + throw new IllegalArgumentException("Script "+script+" does not exist."); + } + this.script = script; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#execute() + */ + /** + * @throws InterruptedException + * @throws RuntimeException Script did not exit with 0, + * Execution was interrupted, + * IO problem occurred + */ + @Override + public void execute() throws InterruptedException { + try{ + logger.fine("Execute script "+script); + Process process = Runtime.getRuntime().exec(script); + int exitValue = process.waitFor(); + logger.fine("Script ["+script+"] return value: "+exitValue); + + // Check script exit value to 0 if != 0 then throw an runtime exception + if(exitValue != 0){ + throw new RuntimeException("Script ["+script+"] returned with an exit value not equal to 0"); + } + } + catch(IOException e){ + // Convert Exception into unchecked RuntimeException + throw new RuntimeException("Unable to execute script: "+script,e); + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#abort() + */ + @Override + public void abort() { + // This action cannot be aborted, therefore this function is not implemented. + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#destroy() + */ + @Override + public void destroy() { + // Nothing to be done + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/ChannelAccessFunctionActuator.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/ChannelAccessFunctionActuator.java new file mode 100644 index 0000000..478da04 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/ChannelAccessFunctionActuator.java @@ -0,0 +1,350 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actors; + +import gov.aps.jca.CAException; + +import java.util.logging.Logger; + +import ch.psi.fda.core.Actor; +import ch.psi.fda.core.EngineConfiguration; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * This actuator sets an Channel Access channel from a start to an end value by doing discrete steps. + * @author ebner + * + */ +public class ChannelAccessFunctionActuator implements Actor { + + // Get Logger + private static Logger logger = Logger.getLogger(ChannelAccessFunctionActuator.class.getName()); + + private boolean asynchronous = false; + + /** + * Start value of the actuator + */ + private double start; + + /** + * End value of the actuator + */ + private double end; + + /** + * Step size of the move + */ + private double stepSize; + + /** + * Move direction (start<end = 1, start>end = -1) + */ + private int direction; + + /** + * Execution count of actuator. This variable is used to minimize the floating point + * rounding errors for calculating the next step. + */ + private int count; + + /** + * Flag that indicates whether there is a next set value for the Actor + */ + private boolean next; + + /** + * Value to set at next @see ch.psi.fda.engine.Actor#set() call + */ + private double value; + + /** + * Level of accuracy the positioner need to have (e.g. if a positioner is set to 1 the readback set value + * of the positioner need to have at lease 1+/-accuracy) + * Default is stepSize/2 + */ + private double accuracy; + + /** + * Channel Access channel of the actuator + */ + private ChannelBean channel; + + /** + * Channel Access channel of the actuator + */ + private ChannelBean doneChannel = null; + + private final T doneValue; + private final long doneDelay; + + private final double originalStart; + private final double originalEnd; + private final int originalDirection; + + + /** + * Move timeout + */ + private Long timeout; + + private final Function function; + + /** + * Constructor - Initialize actor + * @param channelName + * @param start + * @param end + * @param stepSize + * @param timeout Maximum move time (in milliseconds) + */ + public ChannelAccessFunctionActuator(String channelName, Function function, double start, double end, double stepSize, Long timeout){ + this(channelName, null, null, 0, function, start, end, stepSize, timeout); + } + + /** + * Constructor + * @param channelName + * @param doneChannelName If null actor will not wait (for this channel) to continue + * @param doneValue + * @param doneDelay Delay in seconds before checking the done channel + * @param start + * @param end + * @param stepSize + * @param timeout Maximum move time (in milliseconds) + */ + @SuppressWarnings("unchecked") + public ChannelAccessFunctionActuator(String channelName, String doneChannelName, T doneValue, double doneDelay, Function function, double start, double end, double stepSize, Long timeout){ + + this.doneValue = doneValue; + this.doneDelay = (long) Math.floor((doneDelay*1000)); + this.start = start; + this.end = end; + + if(stepSize <= 0){ + throw new IllegalArgumentException("Step size ["+stepSize+"] must be > 0"); + } + this.stepSize = stepSize; + + this.accuracy = stepSize/2; + + // Validate and save timeout parameter + if(timeout!=null && timeout<=0){ + throw new IllegalArgumentException("Timeout must be >0 or null"); + } + else{ + this.timeout = timeout; + } + + if(function==null){ + throw new IllegalArgumentException("Function must not be null"); + } + this.function = function; + + + init(); + + // Save original settings + this.originalStart = start; + this.originalEnd = end; + this.originalDirection = direction; + + + // Initialize/create Channel Access channel + try { + channel = ChannelBeanFactory.getFactory().createChannelBean(Double.class, channelName, false); + } catch (CAException e) { + // Convert Exception into unchecked RuntimeException + throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+channelName+"]",e); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to initialize actuator channel [name:"+channelName+"]",e); + } + if(doneChannelName != null){ + try { + doneChannel = ChannelBeanFactory.getFactory().createChannelBean((Class)doneValue.getClass(), doneChannelName, false); + } catch (CAException e) { + // Convert Exception into unchecked RuntimeException + throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+doneChannelName+"]",e); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to initialize actuator channel [name:"+doneChannelName+"]",e); + } + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.engine.Actor#set() + */ + @Override + public void set() throws InterruptedException { + + // Throw an IllegalStateException in the case that set is called although there is no next step. + if(!next){ + throw new IllegalStateException("The actuator does not have any next step."); + } + + // Set actuator channel + logger.finest("Set actuator channel "+channel.getName()+" to value: "+value); + try { + double fvalue = function.calculate(value); + + if(!asynchronous){ + if(timeout==null){ + channel.setValue(fvalue); + } + else{ + channel.setValue(fvalue, timeout); + } + } + else{ + channel.setValueNoWait(fvalue); + } + + if(doneChannel != null){ + Thread.sleep(doneDelay); + doneChannel.waitForValue(doneValue); + } + + // Check whether the set value is really on the value that was set before. + if(EngineConfiguration.getInstance().isCheckActorSet()){ + double c = channel.getValue(true); + double a = Math.abs( c - fvalue ); + if ( a > accuracy ){ + throw new RuntimeException("Actor could not be set to the value "+fvalue+" The readback of the set value does not match the value that was set [value: "+c+" delta: "+a+" accuracy: "+accuracy+"]"); + } + } + + } catch (CAException e) { + // Convert Exception into unchecked RuntimeException + throw new RuntimeException("Unable to move actuator [channel: "+channel.getName()+"] to value "+value,e); + } + + + count++; + double nextValue = start+(count*stepSize*direction); // Done like this to keep floating point rounding errors minimal + + if((direction==1&&nextValue<=end)||(direction==-1&nextValue>=end)){ + + // Apply function +// nextValue = function.calculate(nextValue); + + logger.fine("Next actor value: "+nextValue); + value=nextValue; + this.next = true; + } + else{ + // There is no next set value + this.next = false; + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.engine.Actor#hasNext() + */ + @Override + public boolean hasNext() { + return next; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#init() + */ + @Override + public void init() { + this.count = 0; + + // Determine move direction + this.direction = 1; + if(start>end){ + direction=-1; // Move in negative direction + } + + + // Set first set value to the start value + this.value = start; + this.next = true; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#reverse() + */ + @Override + public synchronized void reverse() { + double oldStart = start; + this.start = this.end; + this.end = oldStart; + + // Determine move direction + this.direction = 1; + if(this.start>this.end){ + direction=-1; // Move in negative direction + } + + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#reset() + */ + @Override + public void reset() { + this.start = this.originalStart; + this.end = this.originalEnd; + this.direction = this.originalDirection; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#destroy() + */ + @Override + public void destroy() { + // Destroy channel + try { + logger.finest("Destroy actor channel: "+channel.getName()); + channel.destroy(); + } catch (CAException e) { + throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e); + } + + // Destroy done channel if exists + if(doneChannel != null){ + try { + logger.finest("Destroy actor done channel: "+doneChannel.getName()); + doneChannel.destroy(); + } catch (CAException e) { + throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e); + } + } + } + + /** + * @return the asynchronous + */ + public boolean isAsynchronous() { + return asynchronous; + } + + /** + * @param asynchronous the asynchronous to set + */ + public void setAsynchronous(boolean asynchronous) { + this.asynchronous = asynchronous; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/ChannelAccessLinearActuator.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/ChannelAccessLinearActuator.java new file mode 100644 index 0000000..7279eca --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/ChannelAccessLinearActuator.java @@ -0,0 +1,329 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actors; + +import gov.aps.jca.CAException; + +import java.util.logging.Logger; + +import ch.psi.fda.core.Actor; +import ch.psi.fda.core.EngineConfiguration; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * This actuator sets an Channel Access channel from a start to an end value by doing discrete steps. + * @author ebner + * + */ +public class ChannelAccessLinearActuator implements Actor { + + // Get Logger + private static Logger logger = Logger.getLogger(ChannelAccessLinearActuator.class.getName()); + + private boolean asynchronous = false; + + /** + * Start value of the actuator + */ + private double start; + + /** + * End value of the actuator + */ + private double end; + + /** + * Step size of the move + */ + private double stepSize; + + /** + * Move direction (start<end = 1, start>end = -1) + */ + private int direction; + + /** + * Execution count of actuator. This variable is used to minimize the floating point + * rounding errors for calculating the next step. + */ + private int count; + + /** + * Flag that indicates whether there is a next set value for the Actor + */ + private boolean next; + + /** + * Value to set at next @see ch.psi.fda.engine.Actor#set() call + */ + private double value; + + /** + * Level of accuracy the positioner need to have (e.g. if a positioner is set to 1 the readback set value + * of the positioner need to have at lease 1+/-accuracy) + * Default is stepSize/2 + */ + private double accuracy; + + /** + * Channel Access channel of the actuator + */ + private ChannelBean channel; + + /** + * Channel Access channel of the actuator + */ + private ChannelBean doneChannel = null; + + private final T doneValue; + private final long doneDelay; + + private final double originalStart; + private final double originalEnd; + private final int originalDirection; + + + /** + * Move timeout + */ + private Long timeout; + + + /** + * Constructor + * @param channelName + * @param doneChannelName If null actor will not wait (for this channel) to continue + * @param doneValue + * @param doneDelay Delay in seconds before checking the done channel + * @param start + * @param end + * @param stepSize + * @param timeout Maximum move time (in milliseconds) + */ + @SuppressWarnings("unchecked") + public ChannelAccessLinearActuator(String channelName, String doneChannelName, T doneValue, double doneDelay, double start, double end, double stepSize, Long timeout){ + + this.doneValue = doneValue; + this.doneDelay = (long) Math.floor((doneDelay*1000)); + this.start = start; + this.end = end; + + if(stepSize <= 0){ + throw new IllegalArgumentException("Step size ["+stepSize+"] must be > 0"); + } + this.stepSize = stepSize; + + this.accuracy = stepSize/2; + + // Validate and save timeout parameter + if(timeout!=null && timeout<=0){ + throw new IllegalArgumentException("Timeout must be >0 or null"); + } + else{ + this.timeout = timeout; + } + + + init(); + + // Save original settings + this.originalStart = start; + this.originalEnd = end; + this.originalDirection = direction; + + + // Initialize/create Channel Access channel + try { + channel = ChannelBeanFactory.getFactory().createChannelBean(Double.class, channelName, false); + } catch (CAException e) { + // Convert Exception into unchecked RuntimeException + throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+channelName+"]",e); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to initialize actuator channel [name:"+channelName+"]",e); + } + if(doneChannelName != null){ + try { + doneChannel = ChannelBeanFactory.getFactory().createChannelBean((Class) doneValue.getClass(), doneChannelName, false); + } catch (CAException e) { + // Convert Exception into unchecked RuntimeException + throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+doneChannelName+"]",e); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to initialize actuator channel [name:"+doneChannelName+"]",e); + } + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.engine.Actor#set() + */ + @Override + public void set() throws InterruptedException { + + // Throw an IllegalStateException in the case that set is called although there is no next step. + if(!next){ + throw new IllegalStateException("The actuator does not have any next step."); + } + + // Set actuator channel + logger.finest("Set actuator channel "+channel.getName()+" to value: "+value); + try { + + if(!asynchronous){ + if(timeout==null){ + channel.setValue(value); + } + else{ + channel.setValue(value, timeout); + } + } + else{ + channel.setValueNoWait(value); + } + + if(doneChannel != null){ + Thread.sleep(doneDelay); + doneChannel.waitForValue(doneValue); + } + + // Check whether the set value is really on the value that was set before. + if(EngineConfiguration.getInstance().isCheckActorSet()){ + double c = channel.getValue(true); + double a = Math.abs( c - value ); + if ( a > accuracy ){ + throw new RuntimeException("Actor could not be set to the value "+value+" The readback of the set value does not match the value that was set [value: "+c+" delta: "+a+" accuracy: "+accuracy+"]"); + } + } + + } catch (CAException e) { + // Convert Exception into unchecked RuntimeException + throw new RuntimeException("Unable to move actuator [channel: "+channel.getName()+"] to value "+value,e); + } + + + count++; + double nextValue = start+(count*stepSize*direction); // Done like this to keep floating point rounding errors minimal + + if((direction==1&&nextValue<=end)||(direction==-1&nextValue>=end)){ + logger.fine("Next actor value: "+nextValue); + value=nextValue; + this.next = true; + } + else{ + // There is no next set value + this.next = false; + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.engine.Actor#hasNext() + */ + @Override + public boolean hasNext() { + return next; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#init() + */ + @Override + public void init() { + this.count = 0; + + // Determine move direction + this.direction = 1; + if(start>end){ + direction=-1; // Move in negative direction + } + + + // Set first set value to the start value + this.value = start; + this.next = true; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#reverse() + */ + @Override + public synchronized void reverse() { + double oldStart = start; + this.start = this.end; + this.end = oldStart; + + // Determine move direction + this.direction = 1; + if(this.start>this.end){ + direction=-1; // Move in negative direction + } + + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#reset() + */ + @Override + public void reset() { + this.start = this.originalStart; + this.end = this.originalEnd; + this.direction = this.originalDirection; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#destroy() + */ + @Override + public void destroy() { + // Destroy channel + try { + logger.finest("Destroy actor channel: "+channel.getName()); + channel.destroy(); + } catch (CAException e) { + throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e); + } + + // Destroy done channel if exists + if(doneChannel != null){ + try { + logger.finest("Destroy actor done channel: "+doneChannel.getName()); + doneChannel.destroy(); + } catch (CAException e) { + throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e); + } + } + } + + /** + * @return the asynchronous + */ + public boolean isAsynchronous() { + return asynchronous; + } + + /** + * @param asynchronous the asynchronous to set + */ + public void setAsynchronous(boolean asynchronous) { + this.asynchronous = asynchronous; + } + + + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/ChannelAccessTableActuator.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/ChannelAccessTableActuator.java new file mode 100644 index 0000000..fb5b11a --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/ChannelAccessTableActuator.java @@ -0,0 +1,314 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actors; + +import gov.aps.jca.CAException; + +import java.util.logging.Logger; + +import ch.psi.fda.core.Actor; +import ch.psi.fda.core.EngineConfiguration; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * This actuator sets an Channel Access channel by using the positions from the given table. + * @author ebner + * + */ +public class ChannelAccessTableActuator implements Actor { + + // Get Logger + private static Logger logger = Logger.getLogger(ChannelAccessTableActuator.class.getName()); + + private boolean asynchronous = false; + + /** + * Position table + */ + private final double[] table; + + /** + * Level of accuracy the positioner need to have (e.g. if a positioner is set to 1 the readback set value + * of the positioner need to have at lease 1+/-accuracy) + */ + private double accuracy = 0.1; + + /** + * Execution count of actuator. This variable is used to minimize the floating point + * rounding errors for calculating the next step. + */ + private int count; + + /** + * Flag that indicates whether there is a next set value for the Actor + */ + private boolean next; + + + /** + * Channel Access channel of the actuator + */ + private ChannelBean channel; + + /** + * Channel Access channel of the actuator + */ + private ChannelBean doneChannel = null; + + private final T doneValue; + private final long doneDelay; + + /** + * Flag that indicates whether the actor moves in the positive direction + */ + private boolean positiveDirection = true; + private final boolean originalPositiveDirection; + + /** + * Maximum move time (in milliseconds) + */ + private Long timeout = null; + + + /** + * Constructor - Initialize actor + * @param channelName Name of the channel to set + * @param table Position table with the explicit positions for each step + * @param timeout Maximum move time (in milliseconds) + */ + public ChannelAccessTableActuator(String channelName, double[] table, Long timeout){ + this(channelName, null, null, 0, table, timeout); + } + + /** + * Constructor + * @param channelName + * @param doneChannelName + * @param doneValue + * @param doneDelay + * @param table + * @param timeout Maximum move time (in milliseconds) + */ + @SuppressWarnings("unchecked") + public ChannelAccessTableActuator(String channelName, String doneChannelName, T doneValue, double doneDelay, double[] table, Long timeout){ + + this.doneValue = doneValue; + this.doneDelay = (long) Math.floor((doneDelay*1000)); + + if(table==null){ + throw new IllegalArgumentException("Null table is not accepted"); + } + if(table.length==0){ + throw new IllegalArgumentException("Position table need to have at least one position"); + } + + this.table = table; + + // Validate and save timeout parameter + if(timeout!=null && timeout<=0){ + throw new IllegalArgumentException("Timeout must be >0 or null"); + } + else{ + this.timeout = timeout; + } + + init(); + + // Save the initial direction + this.originalPositiveDirection = positiveDirection; + + // Initialize/create Channel Access channel + try { + channel = ChannelBeanFactory.getFactory().createChannelBean(Double.class, channelName, false); + } catch (CAException e) { + // Convert Exception into unchecked RuntimeException + throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+channelName+"]",e); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to initialize actuator channel [name:"+channelName+"]",e); + } + + if(doneChannelName != null){ + try { + doneChannel = ChannelBeanFactory.getFactory().createChannelBean((Class)doneValue.getClass(), doneChannelName, false); + } catch (CAException e) { + // Convert Exception into unchecked RuntimeException + throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+doneChannelName+"]",e); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to initialize actuator channel [name:"+doneChannelName+"]",e); + } + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.engine.Actor#set() + */ + @Override + public void set() throws InterruptedException { + + // Throw an IllegalStateException in the case that set is called although there is no next step. + if(!next){ + throw new IllegalStateException("The actuator does not have any next step."); + } + + // Set actuator channel + logger.finest("Set actuator channel "+channel.getName()+" to value: "+table[count]); + try { + if(!asynchronous){ + if(timeout==null){ + channel.setValue(table[count]); + } + else{ + channel.setValue(table[count], timeout); + } + } + else{ + channel.setValueNoWait(table[count]); + } + + + if(doneChannel != null){ + Thread.sleep(doneDelay); + doneChannel.waitForValue(doneValue); + } + + // Check whether the set value is really on the value that was set before. + if(EngineConfiguration.getInstance().isCheckActorSet()){ + if ( Math.abs( channel.getValue() - table[count] ) > accuracy ){ + throw new RuntimeException("Actor could not be set to the value "+table[count]+" The readback of the set value does not match the value that was set"); + } + } + + } catch (CAException e) { + // Convert Exception into unchecked RuntimeException + throw new RuntimeException("Move actuator [channel: "+channel.getName()+"] to value "+table[count],e); + } + + if(positiveDirection){ + count++; + if(count=0){ + this.next = true; + } + else{ + // There is no next set value + this.next = false; + } + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.engine.Actor#hasNext() + */ + @Override + public boolean hasNext() { + return next; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#init() + */ + @Override + public void init() { + + // Set first set value to the start value + if(positiveDirection){ + this.count = 0; // Set count to the first element + } + else{ + this.count = table.length-1; // Set count to the last element + } + + if(table.length>0){ + this.next = true; + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#reverse() + */ + @Override + public void reverse() { + if(positiveDirection){ + positiveDirection=false; + } + else{ + positiveDirection=true; + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#reset() + */ + @Override + public void reset() { + this.positiveDirection = this.originalPositiveDirection; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#destroy() + */ + @Override + public void destroy() { + // Destroy channel + try { + logger.finest("Destroy actor channel: "+channel.getName()); + channel.destroy(); + } catch (CAException e) { + throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e); + } + + // Destroy done channel if exists + if(doneChannel != null){ + try { + logger.finest("Destroy actor done channel: "+doneChannel.getName()); + doneChannel.destroy(); + } catch (CAException e) { + throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e); + } + } + } + + /** + * @return the asynchronous + */ + public boolean isAsynchronous() { + return asynchronous; + } + + /** + * @param asynchronous the asynchronous to set + */ + public void setAsynchronous(boolean asynchronous) { + this.asynchronous = asynchronous; + } + + + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/ComplexActuator.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/ComplexActuator.java new file mode 100644 index 0000000..393da75 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/ComplexActuator.java @@ -0,0 +1,261 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actors; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +import ch.psi.fda.core.Action; +import ch.psi.fda.core.Actor; + +/** + * Complex actuator that consists of a list of other actuators. The steps of this actor are composed out of the + * steps of the actors that this actuator consists of. + * First all the steps of the first actor are used, after that the steps of the next actor are done. + * Before the first step of an actor pre actions are available and after the last step of an actor a post action is available. + * + * @author ebner + * + */ +public class ComplexActuator implements Actor { + + // Get Logger + private static Logger logger = Logger.getLogger(ComplexActuator.class.getName()); + + /** + * List of actors this actor is made of + */ + private final List actors; + + /** + * Actions that are executed directly before the first step of this actor + */ + private final List preActions; + + /** + * Actions that are executed directly after the last step of this actor + */ + private final List postActions; + + /** + * Flag that indicates whether there is a next set value for the Actor + */ + private boolean next; + + /** + * Index of the actor currently used + */ + private int actualActorCount; + + /** + * Actor currently used to perform steps + */ + private Actor actualActor; + + /** + * Flag to indicate the first set() execution of this actor. + * This flag is used to decide whether to execute the pre actions. + */ + private boolean firstrun; + + /** + * Constructor + */ + public ComplexActuator(){ + this.actors = new ArrayList(); + this.preActions = new ArrayList(); + this.postActions = new ArrayList(); + + this.next = false; + this.actualActorCount = 0; + + this.firstrun = true; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#set() + */ + @Override + public void set() throws InterruptedException { + // Throw an IllegalStateException in the case that set is called although there is no next step. + if(!next){ + throw new IllegalStateException("The actuator does not have any next step."); + } + + // PRE actions + if(firstrun){ + this.firstrun = false; + + // Execute pre actions + logger.finest("Execute pre actions"); + for(Action action: preActions){ + action.execute(); + } + } + + actualActor.set(); // If the actor has no next step then something is wrong in the init/next step logic + + // If the last point of the actual actuator is set take the next actuator in the list if there is one available + while(!actualActor.hasNext()&&actualActorCount getActors() { + return actors; + } + + /** + * @return the preActions + */ + public List getPreActions() { + return preActions; + } + + /** + * @return the postActions + */ + public List getPostActions() { + return postActions; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#destroy() + */ + @Override + public void destroy() { + // Destroy preActions + for(Action a: preActions){ + a.destroy(); + } + + // Destroy actors + for(Actor a: actors){ + a.destroy(); + } + + // Destroy postActions + for(Action a: postActions){ + a.destroy(); + } + } + + + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/Function.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/Function.java new file mode 100644 index 0000000..e4689f4 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/Function.java @@ -0,0 +1,28 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actors; + +/** + * @author ebner + * + */ +public interface Function { + public double calculate(double parameter); +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/JythonFunction.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/JythonFunction.java new file mode 100644 index 0000000..85d5024 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/JythonFunction.java @@ -0,0 +1,113 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actors; + +import java.util.Map; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +import ch.psi.fda.core.scripting.JythonGlobalVariable; + +/** + * @author ebner + * + */ +public class JythonFunction implements Function { + + // Get Logger + private static final Logger logger = Logger.getLogger(JythonFunction.class.getName()); + + public static final String ENTRY_FUNCTION_NAME = "calculate"; + private static final String ENTRY_FUNCTION_PATTERN = "def "+ENTRY_FUNCTION_NAME+"\\((.*)\\):"; + + /** + * Script engine of the manipulator + */ + private ScriptEngine engine; + + private String additionalParameter = ""; + + + public JythonFunction(String script, Map map){ + // Create new script engine + this.engine = new ScriptEngineManager().getEngineByName("python"); + + // Determine script entry function and the function parameters + String[] parameter; + Pattern pattern = Pattern.compile(ENTRY_FUNCTION_PATTERN); + Matcher matcher = pattern.matcher(script); + if(matcher.find() && matcher.groupCount()==1){ + if(!matcher.group(1).trim().equals("")){ + logger.finest("Entry function '"+ENTRY_FUNCTION_PATTERN+"' found - Identified parameters: "+matcher.group(1)); + parameter = matcher.group(1).split(" *, *"); + } + else{ + parameter = new String[0]; + } + } + else{ + throw new IllegalArgumentException("Cannot determine entry function: "+ENTRY_FUNCTION_PATTERN); + } + + // Check whether all parameters are mapped + StringBuilder b = new StringBuilder(); + for(int i=1;i. + * + */ + +package ch.psi.fda.core.actors; + +import ch.psi.fda.core.Actor; + +/** + * Special actuator for the OTFLoop. This type of actor must not be used in any other + * type of loop than OTFLoop. If it is used it will, depending on the loop, immediately stop the loop + * as it no single step. + * @author ebner + * + */ +public class OTFActuator implements Actor { + + /** + * Name of the motor channel + */ + private final String name; + + /** + * Channel name of the encoder; + */ + private final String readback; + + private double start; + private double end; + private final double stepSize; + private final double integrationTime; + + private final String id; + + /** + * Additional backlash for the motor + */ + private final double additionalBacklash; + + + public OTFActuator(String id, String name, String readback, double start, double end, double stepSize, double integrationTime, double additionalBacklash){ + this.id = id; + this.name = name; + this.readback = readback; + this.start = start; + this.end = end; + this.stepSize = stepSize; + this.integrationTime = integrationTime; + this.additionalBacklash = additionalBacklash; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#set() + */ + @Override + public void set() { + // Do nothing + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#hasNext() + */ + @Override + public boolean hasNext() { + return false; + } + + + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @return the readback + */ + public String getReadback() { + return readback; + } + + /** + * @return the start + */ + public double getStart() { + return start; + } + + /** + * @return the end + */ + public double getEnd() { + return end; + } + + /** + * @return the stepSize + */ + public double getStepSize() { + return stepSize; + } + + /** + * @return the integrationTime + */ + public double getIntegrationTime() { + return integrationTime; + } + + /** + * @return the additionalBacklash + */ + public double getAdditionalBacklash() { + return additionalBacklash; + } + + /** + * @return the id + */ + public String getId() { + return id; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#init() + */ + @Override + public void init() { + // Not implemented + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#reverse() + */ + @Override + public void reverse() { + // Not implemented + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#reset() + */ + @Override + public void reset() { + // Not implemented + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#destroy() + */ + @Override + public void destroy() { + // Nothing to be done + + } + + /** + * @param start the start to set + */ + public void setStart(double start) { + this.start = start; + } + + /** + * @param end the end to set + */ + public void setEnd(double end) { + this.end = end; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/PseudoActuatorSensor.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/PseudoActuatorSensor.java new file mode 100644 index 0000000..50b6d23 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/actors/PseudoActuatorSensor.java @@ -0,0 +1,135 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actors; + +import ch.psi.fda.core.Actor; +import ch.psi.fda.core.Sensor; + +/** + * Pseudo actor that is literally doing nothing for n times + * @author ebner + * + */ +public class PseudoActuatorSensor implements Actor, Sensor { + + /** + * Execution count of actuator. + */ + private int count; + + /** + * Number of counts for this actuator + */ + private final int counts; + + private final String id; + + /** + * Constructor + * @param counts + * @param id Id of the Actor/Sensor + */ + public PseudoActuatorSensor(String id, int counts){ + if(counts < 1){ + throw new IllegalArgumentException("Count ["+counts+"] must be > 0"); + } + this.id = id; + this.counts = counts; + + init(); + } + + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#set() + */ + @Override + public void set() { + + // Throw an IllegalStateException in the case that set is called although there is no next step. + if(!hasNext()){ + throw new IllegalStateException("The actuator does not have any next step."); + } + + count++; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Actor#hasNext() + */ + @Override + public boolean hasNext() { + return (count. + * + */ + +package ch.psi.fda.core.collector; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.Logger; + +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.StreamDelimiterMessage; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; + +/** + * Collector class that is collecting and merging data from different Queues. + * @author ebner + * + */ +public class Collector implements Runnable{ + + // Get Logger + private static Logger logger = Logger.getLogger(Collector.class.getName()); + + /** + * List of queues and their metadata this collector is responsible to readout. + * The logic is as follows: + * Read out the last queue until the first queue in the list has the EndOfLoopMessage. + */ + private List queues; + + /** + * Outgoing queue of this collector + */ + private BlockingQueue outQueue; + + /** + * Constructor + */ + public Collector(){ + queues = new ArrayList(); + outQueue = new LinkedBlockingQueue(1000); // Create bounded queue to prevent running out of memory ... + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + + if(queues.size()>0){ + try{ + readQueue(0, null); + } + catch(InterruptedException e){ + // Readout aborted through interrupt + } + } + else{ + // No queue registered for reading + } + + try { + + outQueue.put(new EndOfStreamMessage()); + + } catch (InterruptedException e) { + // TODO Stop loop and exit logic instead of throwing an Exception + throw new RuntimeException("Unable to terminate stream with and End of Stream Message",e); + } + + logger.info("END"); + + } + + + private void readQueue(int index, DataMessage m) throws InterruptedException{ + + BlockingQueue q = queues.get(index).getQueue(); + + // Read Message + Message message = q.take(); + while(message instanceof DataMessage){ + // Create new data message + DataMessage dm = new DataMessage(); + if(m!=null){ + dm.getData().addAll(m.getData()); + } + + dm.getData().addAll(((DataMessage)message).getData()); + + if(index<(queues.size()-1)){ + readQueue(index+1, dm); + } + else{ + // Write message to outgoing queue + outQueue.put(dm); + } + + // Read next message + message = q.take(); + } + + if(message instanceof EndOfStreamMessage){ + // Translate EndOfStream to StreamDelimiter message + StreamDelimiterMessage ddm = new StreamDelimiterMessage(queues.size()-1-index, ((EndOfStreamMessage)message).isIflag()); + // Write message to outgoing queue + outQueue.put(ddm); + } + } + + + /** + * @return the queues + */ + public List getQueues() { + return queues; + } + + + /** + * Get the outgoing data queue. + * Attention, only call this method after all ingoing queues were registered! Otherwise the data returned + * by this method is not accurate. + * @return output queue of collector + */ + public DataQueue getOutQueue(){ + DataMessageMetadata dataMessageMetadata = new DataMessageMetadata(); + dataMessageMetadata.getComponents(); + + // Generate new combined metadata and add dimension information to the components + int nq = queues.size(); + for(int i=0;i. + * + */ + +package ch.psi.fda.core.collector; + +import java.util.ArrayList; +import java.util.List; + +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; + +/** + * Serialize data received by a DataQueue + * @author ebner + * + */ +public class DataDispatcher implements Runnable{ + + private DataQueue queue; + private List outQueues; + + public DataDispatcher(DataQueue queue){ + this.queue = queue; + this.outQueues = new ArrayList(); + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + try{ + + // TODO Need to synchronize message metadata +// for(DataQueue q: outQueues){ +// } + + // Dispatch Messages + Message message = queue.getQueue().take(); + while(!(message instanceof EndOfStreamMessage)){ + + // Clone message ... + for(DataQueue q: outQueues){ + q.getQueue().put(message); + } + + // Read next message + message = queue.getQueue().take(); + } + + // Write end of stream message + for(DataQueue q: outQueues){ + q.getQueue().put(message); + } + + } catch (InterruptedException e) { + // TODO Stop loop and exit logic instead of throwing an Exception + throw new RuntimeException("Data serializer was interrupted while writing data to file",e); + } + } + + /** + * @return the outQueues + */ + public List getOutQueues() { + return outQueues; + } + + + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/guard/ChannelAccessGuard.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/guard/ChannelAccessGuard.java new file mode 100644 index 0000000..84144f7 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/guard/ChannelAccessGuard.java @@ -0,0 +1,117 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.guard; + +import gov.aps.jca.CAException; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import ch.psi.fda.core.Guard; + +/** + * Guard checking channels to meet a certain condition + * @author ebner + * + */ +public class ChannelAccessGuard implements Guard { + + + // Get Logger + private static Logger logger = Logger.getLogger(ChannelAccessGuard.class.getName()); + + /** + * Flag to indicate whether a guard condition failed since the last init call + * true: all conditions met, false: at least one condition failed + */ + private boolean check = true; + + private final List conditions; + + /** + * Constructor + * @param conditions + */ + public ChannelAccessGuard(List conditions){ + + this.conditions = conditions; + + // Create channel that contribute to the status of the guard + for(final ChannelAccessGuardCondition condition: conditions){ + condition.getChannel().addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if(! evt.getNewValue().equals(condition.getValue())){ + check=false; + } + } + }); + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Guard#init() + */ + @Override + public void init() { + check = true; + + // Check one time if all conditions are met + for(ChannelAccessGuardCondition condition: conditions){ + try{ + if(! (condition.getChannel().getValue(true)).equals(condition.getValue()) ){ + check=false; + // Early exit + break; + } + } + catch(CAException e){ + logger.log(Level.WARNING, "Unable ", e); + check=false; + } catch (InterruptedException e) { + throw new RuntimeException("Guard interrupted ",e); + } + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Guard#check() + */ + @Override + public boolean check() { + return check; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Guard#destroy() + */ + @Override + public void destroy() { + // Destroy Guard Conditions + for(ChannelAccessGuardCondition condition: conditions){ + condition.destroy(); + } + + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/guard/ChannelAccessGuardCondition.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/guard/ChannelAccessGuardCondition.java new file mode 100644 index 0000000..a844d11 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/guard/ChannelAccessGuardCondition.java @@ -0,0 +1,96 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.guard; + +import java.util.logging.Logger; + +import gov.aps.jca.CAException; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * Channel and condition that need to be met. + * @author ebner + * + */ +public class ChannelAccessGuardCondition { + + // Get Logger + private static Logger logger = Logger.getLogger(ChannelAccessGuardCondition.class.getName()); + + /** + * Channel name + */ + private final ChannelBean channel; + + /** + * Value of the channel to meet condition + */ + private final Object value; + + /** + * Constructor + * @param channel Name of the channel that contributes to a guard + * @param value + */ + public ChannelAccessGuardCondition(String channel, Object value){ + try { + this.channel = ChannelBeanFactory.getFactory().createChannelBean(value.getClass(), channel, true); + } catch (CAException e) { + // Convert Exception into unchecked RuntimeException + throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+channel+"]",e); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to initialize actuator channel [name:"+channel+"]",e); + } + this.value = value; + } + + /** + * @return the channel + */ + public ChannelBean getChannel() { + return channel; + } + + /** + * @return the value + */ + public Object getValue() { + return value; + } + + /** + * Destroy guard condition. + * Can be used for the cleanup of used resources of the guard condition (e.g. to close connections, ...) if + * this cannot be done automatically by the GarbageCollector. + * + * After calling this method the guard condition must not be used any more! + */ + public void destroy(){ + // Destroy channel + try { + logger.finest("Destroy guard condition channel: "+channel.getName()); + channel.destroy(); + } catch (CAException e) { + throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e); + } + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/ActorSensorLoop.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/ActorSensorLoop.java new file mode 100644 index 0000000..0d3978d --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/ActorSensorLoop.java @@ -0,0 +1,564 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.loops; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.Logger; + +import ch.psi.fda.core.Action; +import ch.psi.fda.core.ActionLoop; +import ch.psi.fda.core.Actor; +import ch.psi.fda.core.ActorSetCallable; +import ch.psi.fda.core.Guard; +import ch.psi.fda.core.Sensor; +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; + +/** + * Loop of actions to accomplish a task or test. + * @author ebner + * + */ +public class ActorSensorLoop implements ActionLoop { + + // Get Logger + private static Logger logger = Logger.getLogger(ActorSensorLoop.class.getName()); + + /** + * Flag to indicate whether the data of this loop will be grouped + * According to this flag the dataGroup flag in EndOfStream will be set. + */ + private boolean dataGroup = false; + + /** + * List of additional action loops that are executed at the end of each + * iteration of the loop + */ + private List actionLoops; + /** + * List of actions that are executed at the beginning of the loop. + */ + private List preActions; + /** + * List of actions that are executed at the end of the loop. + */ + private List postActions; + + /** + * List of actions that are executed before the actors are set + */ + private List preActorActions; + + /** + * List of actions that are executed after all actors have been set + */ + private List postActorActions; + + /** + * List of actions that are executed before the sensors are read out + */ + private List preSensorActions; + /** + * List of actions that are executed after all sensors have been read out + */ + private List postSensorActions; + + /** + * List of actors of this loop + */ + private List actors; + + /** + * List of sensors of this loop + */ + private List sensors; + + /** + * Data queue sensor data is posted to. A message consists of a list of data objects + * that are read out of the sensors of this loop. + */ + private BlockingQueue dataQueue; + + /** + * Guard used to check whether the environment was ok while reading out the sensors + */ + private Guard guard = null; + + private boolean loop = false; + + private final boolean zigZag; + + private List pactors; + + + /** + * Default constructor + */ + public ActorSensorLoop(){ + this(false); + } + + /** + * Create instance of the + */ + public ActorSensorLoop(boolean zigZag){ + this.zigZag = zigZag; + this.actionLoops = new ArrayList(); + this.preActions = new ArrayList(); + this.postActions = new ArrayList(); + this.preActorActions = new ArrayList(); + this.postActorActions = new ArrayList(); + this.preSensorActions = new ArrayList(); + this.postSensorActions = new ArrayList(); + + this.actors = new ArrayList(); + this.pactors = new ArrayList(); + this.sensors = new ArrayList(); + + this.dataQueue = new LinkedBlockingQueue(1000); + } + + + /* (non-Javadoc) + * @see ch.psi.fda.engine.ActionLoop#execute() + */ + /** + * Executes the actor sensor loop. The actor sensor loop is build up as follows: + * preActions + * loop{ + * check actors - abort if there are no new steps + * pre actor actions + * set actors + * post actor actions + * pre sensor actions + * read sensors + * post sensor actions + * execute additional registered action loops + * } + * postActions + * @throws InterruptedException + */ + @Override + public void execute() throws InterruptedException { + /** + * Thread pool for parallel execution of tasks + */ + ExecutorService executorService = Executors.newCachedThreadPool(); + + loop = true; + + // Execute pre actions + for(Action action: preActions){ + action.execute(); + } + + // Initialize actors of Loop + for(Actor actor: actors){ + actor.init(); + } + + // Variable to store the last guard status + boolean guardOK=true; + + // Execute loop logic + while(loop){ + + if(guardOK){ + + + // If actors are defined for the loop check whether all of them + // have a next step defined if there is no actor defined only run this loop once + if(actors.size()>0){ + // Check whether the actors of this loop have a next step. If not + // abort the loop + boolean hasNext = true; + for(Actor actor: actors){ + if(!actor.hasNext()){ + hasNext=false; + break; // Stop actor check loop + } + } + + // If not all actors have a next step abort the loop + if(!hasNext){ + break; // Stop action loop + } + } + else{ + // No actors defined, only run loop once + loop = false; + } + + + + // Execute pre actor actions + for(Action action: preActorActions){ + action.execute(); + } + + // Set actors +// for(Actor actor: actors){ +// actor.set(); +// } + // Parallel set of the actors + try { + for (Future f : executorService.invokeAll(pactors)) { + f.get(); //Blocks until the async set() is finished + } + } catch (ExecutionException e) { + throw new RuntimeException("Setting the actors failed",e); + } + + // Execute post actor actions + for(Action action: postActorActions){ + action.execute(); + } + + } + + if(guard!=null){ + // Initialize guard + guard.init(); + guardOK=guard.check(); + + // Wait until guard is ok + while(!guardOK){ + logger.info("Waiting for guard condition(s) to be met"); + // Sleep 100 milliseconds before next check + Thread.sleep(1000); + + // Check whether the loop is not aborted, if it is aborted + // break the wait loop (afterwards also the loop loop is aborted) + if(!loop){ + break; + } + + guard.init(); + guardOK=guard.check(); + } + + // If loop is aborted proceed to next iteration an abort loop + if(!loop){ + continue; + } + } + + // Execute pre sensor actions + for(Action action: preSensorActions){ + action.execute(); + } + + // Read sensors + DataMessage message = new DataMessage(); + for(Sensor sensor: sensors){ + // Readout sensor + Object o = sensor.read(); + // Add sensor data item to message + message.getData().add(o); + } + + // Execute post sensor actions + for(Action action: postSensorActions){ + action.execute(); + } + + // Check guard if one is registered + if(guard!=null){ + guardOK=guard.check(); + } + + if(guardOK){ + + // Post a message with the sensor data + dataQueue.put(message); + + // Loop all configured ActionLoop objects + for(ActionLoop actionLoop: actionLoops){ + actionLoop.execute(); + } + } + } + + // Execute post actions + for(Action action: postActions){ + action.execute(); + } + + + // Issue end of loop control message + // Set iflag of the EndOfStreamMessage according to dataGroup flag of this loop + dataQueue.put(new EndOfStreamMessage(dataGroup)); + + if(zigZag){ + // Reverse actors for the next run + for(Actor actor: actors){ + actor.reverse(); + } + } + + executorService.shutdownNow(); + + } + + /* (non-Javadoc) + * @see ch.psi.fda.engine.ActionLoop#abort() + */ + @Override + public void abort(){ + loop = false; + + // To abort all wait actions interrupt this thread + for(Action a: preSensorActions){ + a.abort(); + } + + // Recursively abort all registered action loops + for(ActionLoop actionLoop: actionLoops){ + actionLoop.abort(); + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#prepare() + */ + @Override + public void prepare() { + // Put in prepare code here + + // Create callable for all actors + pactors.clear(); + for(final Actor a: actors){ + pactors.add(new ActorSetCallable(a)); + } + + // Reset all actuators + for(Actor a: actors){ + a.reset(); + } + + // Recursively call prepare() method of all registered action loops + for(ActionLoop actionLoop: actionLoops){ + actionLoop.prepare(); + } + } + + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#cleanup() + */ + @Override + public void cleanup() { + // Put in cleanup code here + + // Recursively call cleanup() method of all registered action loops + for(ActionLoop actionLoop: actionLoops){ + actionLoop.cleanup(); + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#destroy() + */ + @Override + public void destroy() { + + // Call destroy method of sub components + for(Action a: preActions){ + logger.finest("Destroy pre-action"); + a.destroy(); + } + + for(Action a: postActions){ + logger.finest("Destroy post-action"); + a.destroy(); + } + + for(Action a: preActorActions){ + logger.finest("Destroy pre-actor action"); + a.destroy(); + } + + for(Action a: postActorActions){ + logger.finest("Destroy post-actor action"); + a.destroy(); + } + + for(Action a: preSensorActions){ + logger.finest("Destroy pre-sensor action"); + a.destroy(); + } + + for(Action a: postSensorActions){ + logger.finest("Destroy post-sensor action"); + a.destroy(); + } + + for(Actor a: actors){ + logger.finest("Destroy actor"); + a.destroy(); + } + + for(Sensor s: sensors){ + logger.finest("Destroy sensor"); + s.destroy(); + } + + if(guard != null){ + logger.finest("Destroy guard"); + guard.destroy(); + } + + // Recursively call cleanup() method of all registered action loops + for(ActionLoop actionLoop: actionLoops){ + logger.finest("Destroy action loop"); + actionLoop.destroy(); + } + } + + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#getPreActions() + */ + @Override + public List getPreActions() { + return preActions; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#getPostActions() + */ + @Override + public List getPostActions() { + return postActions; + } + + + /** + * The structure of the data message depends on the sensors registered at this loop + * at the time this method is called. + * @return the data queue and the metadata of the data messages + */ + public DataQueue getDataQueue() { + DataMessageMetadata m = new DataMessageMetadata(); + + // Build up data message metadata based on the sensors currently registered. + for(Sensor s: sensors){ + m.getComponents().add(new ComponentMetadata(s.getId())); + } + return new DataQueue(dataQueue, m); + } + + // Getter functions for variable parts + + /** + * @return the actionLoops + */ + public List getActionLoops() { + return actionLoops; + } + + + /** + * @return the preActorActions + */ + public List getPreActorActions() { + return preActorActions; + } + + + /** + * @return the postActorActions + */ + public List getPostActorActions() { + return postActorActions; + } + + + /** + * @return the preSensorActions + */ + public List getPreSensorActions() { + return preSensorActions; + } + + + /** + * @return the postSensorActions + */ + public List getPostSensorActions() { + return postSensorActions; + } + + + /** + * @return the actors + */ + public List getActors() { + return actors; + } + + + /** + * @return the sensors + */ + public List getSensors() { + return sensors; + } + + /** + * @return the guard + */ + public Guard getGuard() { + return guard; + } + + /** + * @param guard the guard to set + */ + public void setGuard(Guard guard) { + this.guard = guard; + } + + /** + * @return the dataGroup + */ + public boolean isDataGroup() { + return dataGroup; + } + + /** + * @param dataGroup the dataGroup to set + */ + public void setDataGroup(boolean dataGroup) { + this.dataGroup = dataGroup; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/OTFBean.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/OTFBean.java new file mode 100644 index 0000000..7725ba7 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/OTFBean.java @@ -0,0 +1,770 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.loops; + +import gov.aps.jca.CAException; + +import java.util.List; + +import ch.psi.jcae.annotation.CaChannel; +import ch.psi.jcae.ChannelBean; + +/** + * Bean holding all OTF channels and functionality + * @author ebner + * + */ +public class OTFBean { + + public enum Status { SETUP, INACTIVE, INITIALIZE, ACTIVE, STOP, FAULT, ERROR }; + public enum Command { NONE, START, STOP }; + + private long timeoutMotorOk = 8000; + private long commandTimeout = 20000; // Maximum time until a command should take effect + + @CaChannel(type=String.class, name =":UMOT") + private ChannelBean motor; + + @CaChannel(type=String.class, name=":MENC") + private ChannelBean encoder; + + @CaChannel(type=Double.class, name=":UBEG") + private ChannelBean begin; + + @CaChannel(type=Double.class, name=":UBEG.DRVL") + private ChannelBean beginMin; + + @CaChannel(type=Double.class, name=":UBEG.DRVH") + private ChannelBean beginMax; + + @CaChannel(type=Double.class, name=":UEND") + private ChannelBean end; + + @CaChannel(type=Double.class, name=":UEND.DRVL") + private ChannelBean endMin; + + @CaChannel(type=Double.class, name=":UEND.DRVH") + private ChannelBean endMax; + + @CaChannel(type=Double.class, name=":USSIZ") + private ChannelBean stepSize; + + @CaChannel(type=Double.class, name=":USSIZ.DRVL") + private ChannelBean stepSizeMin; + + @CaChannel(type=Double.class, name=":UITIM") + private ChannelBean integrationTime; + + @CaChannel(type=Double.class, name=":UITIM.DRVL") + private ChannelBean integrationTimeMin; + + @CaChannel(type=Double.class, name=":UITIM.DRVH") + private ChannelBean integrationTimeMax; + + @CaChannel(type=Double.class, name=":UBCL") + private ChannelBean userBacklash; + + @CaChannel(type=String.class, name=":NFSSE") + private ChannelBean nfsServer; + + @CaChannel(type=String.class, name=":NFSSH") + private ChannelBean nfsShare; + + @CaChannel(type=String.class, name=":DFNAM") + private ChannelBean fileName; + + @CaChannel(type=String.class, name=":FFORM") + private ChannelBean fileNameFormat; + + @CaChannel(type=Integer.class, name=":FCNT") + private ChannelBean fileCount; + + @CaChannel(type=Integer.class, name=":FCNT.B") + private ChannelBean resetFileCounter; + + @CaChannel(type=Boolean.class, name=":FAPPE") + private ChannelBean appendFile; + + @CaChannel(type=Boolean.class, name=":FUSE") + private ChannelBean fileNameGeneration; + + @CaChannel(type=Boolean.class, name=":UZIGZ") + private ChannelBean zigZag; + + @CaChannel(type=Integer.class, name=":UCOM") + private ChannelBean command; + + @CaChannel(type=Boolean.class, name=":SCRU", monitor=true) + private ChannelBean scanRunning; + + @CaChannel(type=Boolean.class, name=":MUENC") + private ChannelBean useEncoder; + + @CaChannel(type=String.class, name={":CTM0",":CTM1",":CTM2",":CTM3",":CTM4",":CTM5",":CTM6",":CTM7"}) + private List> monitoredChannels; + + @CaChannel(type=Boolean.class, name=":OTF", monitor=true) + private ChannelBean running; + + @CaChannel(type=Integer.class, name=":USTAT", monitor=true) + private ChannelBean status; + + @CaChannel(type=Boolean.class, name=":MOK", monitor=true) + private ChannelBean motorOk; + + @CaChannel(type=Boolean.class, name=":EOK", monitor=true) + private ChannelBean encoderOk; + + @CaChannel(type=String.class, name=":MSG") + private ChannelBean message; + + /** + * Get the trigger name that can be used by the sscan record to trigger an OTFScan + * @return Name of the trigger that can be used by sscan record + */ + public String getSScanTriggerName(){ + return(running.getName()); + } + + /** + * Start OTF scan + */ + public void start() { + try{ + if(getStatus().equals(Status.INACTIVE)){ + + // Send START command + this.command.setValue(Command.START.ordinal()); + + // Wait until OtF logic is active + this.scanRunning.waitForValue(true, commandTimeout); + } + else{ + throw new RuntimeException("Cannot start scan, status is not INACTIVE.\nThe current status is: "+getStatus()+" . The OTF logic need to be recovered manually [Message: "+getMessage()+"]"); + } + } + catch(Exception e){ + throw new RuntimeException("Unable to start OTF scan.",e); + } + } + + /** + * Abort scan + */ + public void abort() { + try{ + // Send STOP command + this.command.setValue(Command.STOP.ordinal()); + + // Do not wait for put to return + this.running.setValueNoWait(false); + } + catch(Exception e){ + throw new RuntimeException("Unable to abort OTF logic" ,e); + } + } + + /** + * Stop OTF scan + * @throws Exception + */ + public void stop() throws Exception{ + if(!getStatus().equals(Status.INACTIVE) || !getStatus().equals(Status.FAULT)){ + + // Send STOP command + this.command.setValue(Command.STOP.ordinal()); + + // Wait until logic is stopped + status.waitForValue(Status.INACTIVE.ordinal(), commandTimeout); + } + + } + + /** + * Wait until scan has stopped + * @throws InterruptedException + */ + public void waitUntilStopped() throws InterruptedException { + try { + scanRunning.waitForValue(false); // Use of default wait timeout + + // Check whether the status is INACTIVE otherwise get messge from OTF and throw an exception + if(status.getValue() != Status.INACTIVE.ordinal()){ + throw new RuntimeException("OTFSCAN failed with message: "+message.getValue()); + } + + } catch (CAException e) { + throw new RuntimeException("An error occurred while waiting for the OTF logic to finish.", e); + } + } + + /** + * Wait until scan has stopped + * @param waitTimeout + * @throws InterruptedException + */ + public void waitUntilStopped(Long waitTimeout) throws InterruptedException { + try { + scanRunning.waitForValue(false, waitTimeout); + + // Check whether the status is INACTIVE otherwise get messge from OTF and throw an exception + if(status.getValue() != Status.INACTIVE.ordinal()){ + throw new RuntimeException("OTFSCAN failed with message: "+message.getValue()); + } + + } catch (CAException e) { + throw new RuntimeException("An error occurred while waiting for the OTF logic to finish.", e); + } + + + + } + + /** + * Reset OTFScan records to defaults + * @throws CAException + * @throws InterruptedException + */ + public void resetToDefaults() throws CAException, InterruptedException{ + setMonitoredChannels(new String[]{}); + setMotor(""); + begin.setValue(0d); + end.setValue(0d); + stepSize.setValue(0d); + integrationTime.setValue(0d); + zigZag.setValue(false); + setAppendFile(false); + setFileNameGeneration(true); + setFileName(""); + setFileNameFormat("%06d.txt"); + resetFileCounter(); + + setUserBacklash(0d); + +// setNfsServer(""); +// setNfsShare(""); + + waitUntilMotorNotOk(timeoutMotorOk); + } + + /** + * Get motor of the OTFScan axis + * @return Name of the OTF motor + * @throws CAException + */ + public String getMotor() throws CAException, InterruptedException { + return(this.motor.getValue()); + } + + /** + * Set motor of the OTFScan axis + * @param motor + * @throws CAException + */ + public void setMotor(String motor) throws CAException, InterruptedException { + this.motor.setValue(motor); + } + + /** + * Get encoder of the OTFScan axis + * @return Name of the used encoder + * @throws CAException + */ + public String getEncoder() throws CAException, InterruptedException { + return(this.encoder.getValue()); + } + + /** + * Set encoder to use of the OTFScan axis + * @param encoder + * @throws CAException + */ + public void setEncoder(String encoder) throws CAException, InterruptedException { + this.encoder.setValue(encoder); + } + + /** + * Get begin position of the scan + * @return Begin position scan + * @throws CAException + */ + public Double getBegin() throws CAException, InterruptedException { + return(this.begin.getValue()); + } + + /** + * Set begin position of scan + * @param begin + * @throws Exception + */ + public void setBegin(Double begin) throws CAException, InterruptedException { + + if(begin==null){ + throw new IllegalArgumentException("Begin position must not be null"); + } + + if(begin < beginMin.getValue() || begin > beginMax.getValue()){ + throw new IllegalArgumentException("Cannot set begin value to "+begin+ ". Value is outside range [min: "+beginMin.getValue()+" max: "+beginMax.getValue()+"]"); + } + + this.begin.setValue(begin); + } + + /** + * Get minimum value of the begin position + * @return Min value for begin + * @throws CAException + */ + public Double getMinBegin() throws CAException, InterruptedException { + return(this.beginMin.getValue()); + } + + /** + * Get maximum value of the begin position + * @return Max value for begin + * @throws CAException + */ + public Double getMaxBegin() throws CAException, InterruptedException { + return(this.beginMax.getValue()); + } + + /** + * Get end position of the scan + * @return End position scan + * @throws CAException + */ + public Double getEnd() throws CAException, InterruptedException { + return(this.end.getValue()); + } + + /** + * Set end positon of scan + * @param end + * @throws CAException + */ + public void setEnd(Double end) throws CAException, InterruptedException { + + if(end==null){ + throw new IllegalArgumentException("End position must not be null"); + } + + if(end < endMin.getValue() || end > endMax.getValue()){ + throw new IllegalArgumentException("Cannot set end value to "+end+ ". Value is outside range [min: "+endMin.getValue()+" max: "+endMax.getValue()+"]"); + } + + this.end.setValue(end); + } + + /** + * Get minimum value of end position + * @return Min value for end + * @throws CAException + */ + public Double getMinEnd() throws CAException, InterruptedException { + return(this.endMin.getValue()); + } + /** + * Get maximum value of end position + * @return Max value for end + * @throws CAException + */ + public Double getMaxEnd() throws CAException, InterruptedException { + return(this.endMax.getValue()); + } + + /** + * Get scan step size + * @return Step size + * @throws CAException + */ + public Double getStepSize() throws CAException, InterruptedException { + return(this.stepSize.getValue()); + } + + /** + * Set step size of scan + * @param stepSize + * @throws CAException + */ + public void setStepSize(Double stepSize) throws CAException, InterruptedException { + + if(integrationTime==null){ + throw new IllegalArgumentException("Step size must not be null"); + } + + // Check if step size is greater than min step size + if(stepSizeMin.getValue() != 0 && stepSize < stepSizeMin.getValue()){ + throw new IllegalArgumentException("Step size value ["+stepSize+"] is less than minimum step size ["+stepSizeMin.getValue()+"]!"); + } + + this.stepSize.setValue(stepSize); + + // TODO WORKAROUND - Wait to "ensure" that step size related fields are updated (i.e. min/max integration time) + Thread.sleep(1); + } + + /** + * Get minimum integration time + * @return Min value for step size + * @throws CAException + */ + public double getMinStepSize() throws CAException, InterruptedException { + return(this.stepSizeMin.getValue()); + } + + /** + * Get scan integration time (time that is spend in one step) + * @return Integration time + * @throws CAException + */ + public Double getIntegrationTime() throws CAException, InterruptedException { + return(this.integrationTime.getValue()); + } + + /** + * Set integration time of scan + * @param integrationTime + * @throws CAException + */ + public void setIntegrationTime(Double integrationTime) throws CAException, InterruptedException { + + if(integrationTime==null){ + throw new IllegalArgumentException("Integration time must not be null"); + } + + // Check range (if limit is set to 0 then limit is not set) + double min = integrationTimeMin.getValue(); + double max = integrationTimeMax.getValue(); + if(min!= 0 && max!= 0){ + if(integrationTime < min || integrationTime > max){ + throw new IllegalArgumentException("Integration time ["+integrationTime+"] is outside range [min: "+min+" max: "+max+"]"); + } + } + else { + if(min!= 0 && integrationTimemax){ + throw new IllegalArgumentException("Integration time ["+integrationTime+"] is outside range [min: - max: "+max+"]"); + } + } + + this.integrationTime.setValue(integrationTime); + } + + /** + * Get minimum integration time + * @return Min value for integration time + * @throws CAException + */ + public Double getMinIntegrationTime() throws CAException, InterruptedException { + return(this.integrationTimeMin.getValue()); + } + /** + * Get maximum integration time + * @return Max value for integration time + * @throws CAException + */ + public Double getMaxIntegrationTime() throws CAException, InterruptedException { + return(this.integrationTimeMax.getValue()); + } + + /** + * Get additional user defined backlash + * @return User backlash + * @throws CAException + */ + public Double getUserBacklash() throws CAException, InterruptedException { + return(this.userBacklash.getValue()); + } + + /** + * Set additional user defined backlash + * @param userBacklash + * @throws CAException + */ + public void setUserBacklash(Double userBacklash) throws CAException, InterruptedException { + if(userBacklash==null){ + throw new IllegalArgumentException("User backlash must not be null"); + } + + this.userBacklash.setValue(userBacklash); + } + + /** + * Get the current NFS server the data is written to + * @return Name of NFS server + * @throws CAException + */ + public String getNfsServer() throws CAException, InterruptedException { + return(this.nfsServer.getValue()); + } + + /** + * Set name of the NFS server the data is written to + * @param nfsServer + * @throws CAException + */ + public void setNfsServer(String nfsServer) throws CAException, InterruptedException { + this.nfsServer.setValue(nfsServer); + } + + /** + * Get the NFS share the data is written to + * @return Name of NFS share + * @throws CAException + */ + public String getNfsShare() throws CAException, InterruptedException { + return(this.nfsShare.getValue()); + } + + /** + * Set name of the NFS share the data is written to + * @param nfsShare + * @throws CAException + */ + public void setNfsShare(String nfsShare) throws CAException, InterruptedException { + this.nfsShare.setValue(nfsShare); + } + + /** + * Get the name of the data file + * @return Name of data file name + * @throws CAException + */ + public String getFileName() throws CAException, InterruptedException { + return(this.fileName.getValue()); + } + + /** + * Set name of the data file + * @param filename + * @throws CAException + */ + public void setFileName(String filename) throws CAException, InterruptedException { + this.fileName.setValue(filename); + } + + /** + * Get File name formate + * @return Get format for file name + * @throws CAException + */ + public String getFileNameFormat() throws CAException, InterruptedException { + return(this.fileNameFormat.getValue()); + } + + /** + * Set file name formate of the data file + * @param fileNameFormat + * @throws Exception + */ + public void setFileNameFormat(String fileNameFormat) throws CAException, InterruptedException { + this.fileNameFormat.setValue(fileNameFormat); + } + + /** + * Get value of the IOC based file name counter + * @return File counter + * @throws CAException + */ + public int getFileCounter() throws CAException, InterruptedException { + return(this.fileCount.getValue()); + } + + /** + * Reset the IOC based file counter + * @throws CAException + */ + public void resetFileCounter() throws CAException, InterruptedException { + this.resetFileCounter.setValue(1); + } + + /** + * Get if append file option is activated + * @return Append file flag + * @throws CAException + */ + public boolean isAppendFile() throws CAException, InterruptedException { + return(this.appendFile.getValue()); + } + + /** + * Set whether to append the specified file if the file exists + * @param append + * @throws CAException + */ + public void setAppendFile(boolean append) throws CAException, InterruptedException { + this.appendFile.setValue(append); + } + + /** + * Get if file name generation is on or off + * @return File name generation flag + * @throws CAException + */ + public boolean isFileNameGeneration() throws CAException, InterruptedException { + return(this.fileNameGeneration.getValue()); + } + + /** + * Set Whether the file name should be generated out of the file name format and the file counter + * @param generation + * @throws CAException + */ + public void setFileNameGeneration(boolean generation) throws CAException, InterruptedException { + this.fileNameGeneration.setValue(generation); + } + + /** + * Get if ZigZag scan option is on or off + * @return ZigZag flag + * @throws CAException + */ + public boolean isZigZag() throws CAException, InterruptedException { + return(this.zigZag.getValue()); + } + + /** + * Set ZigZag scan mode on/off + * @param zigZag ZigZag mode on = true, ZigZag mode off = false + * @throws CAException + */ + public void setZigZag(boolean zigZag) throws CAException, InterruptedException { + this.zigZag.setValue(zigZag); + } + + /** + * Get whether encoder is used + */ + public boolean isUseEncoder() throws CAException, InterruptedException { + return(this.useEncoder.getValue()); + } + + /** + * Set flag to use encoder + * @throws CAException + */ + public void setUseEncoder(boolean flag) throws CAException, InterruptedException { + this.useEncoder.setValue(flag); + } + + /** + * Get the channels that are currently monitored by the OTFScan logic + * @return Names of the monitored channels + * @throws CAException + */ + public String[] getMonitoredChannels() throws CAException, InterruptedException { + String[] values = new String[this.monitoredChannels.size()]; + + for(int i=0; imonitoredChannels.size()){ + throw new IllegalArgumentException("Only up to "+monitoredChannels.size()+" monitored channels are supported by OTF"); + } + + for(int i=0; i. + * + */ + +package ch.psi.fda.core.loops; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.Logger; + +import jcifs.smb.SmbException; +import jcifs.smb.SmbFile; + +import gov.aps.jca.CAException; +import ch.psi.fda.core.Action; +import ch.psi.fda.core.ActionLoop; +import ch.psi.fda.core.Sensor; +import ch.psi.fda.core.actors.OTFActuator; +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; +import ch.psi.fda.core.sensors.MillisecondTimestampSensor; +import ch.psi.fda.core.sensors.OTFNamedChannelSensor; +import ch.psi.fda.core.sensors.OTFReadbackSensor; +import ch.psi.fda.core.sensors.OTFScalerChannelSensor; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * ActionLoop that is implementing the OTF Scan logic. + * While executing the loop a full OTF scan procedure is executed. + * @author ebner + * + */ +public class OTFLoop implements ActionLoop { + + // Get Logger + private static Logger logger = Logger.getLogger(OTFLoop.class.getName()); + + /** + * Flag to indicate whether the data of this loop will be grouped + * According to this flag the dataGroup flag in EndOfStream will be set. + */ + private boolean dataGroup = false; + + // Constants + /** + * Maximum number of monitored channels + */ + private static final int numberOfMonitoredChannels = 8; + /** + * Maximum number of Scaler channels + */ + private static final int numberOfScalerChannels = 16; + + /** + * Default timeout (in milliseconds) for wait operations + */ + private long timeout = 8000; + + /** + * Name of the NFS server to place the data of the OTF logic + */ + private final String server; + /** + * Share on the NFS server to put the OTF data on to + */ + private final String share; + /** + * SMB share to access the data written by the OTF C logic + */ + private final String smbShare; + + /** + * Flag whether the actor of this loop should move in zig zag mode + */ + private final boolean zigZag; + + /** + * Bean holding all OTF channels and functionality. + */ + private OTFBean obean; + + /** + * List of actions that are executed at the beginning of the loop. + */ + private List preActions; + /** + * List of actions that are executed at the end of the loop. + */ + private List postActions; + + /** + * Special OTF Actuator + */ + private OTFActuator actor = null; + + /** + * List of sensors of this loop + */ + private List sensors; + + + private List dataIndexes; + + /** + * Execution count of this loop. This count is used to determine the + * file name of the OTF file. + */ + private int executionCount; + + /** + * Data queue sensor data is posted to. A message consists of a list of data objects + * that are read out of the sensors of this loop. + */ + private BlockingQueue dataQueue; + + /** + * Flag that indicates that the loop was requested to abort. + */ + private volatile boolean abort = false; + + /** + * Constructor + * @param channelPrefix Prefix of the OTF related records, e.g. MTEST-HW3-OTF + * @param server NFS server the OTF C Logic should put its data to + * @param share Share on NFS server to put the OTF C Logic data + * @param smbShare SMB share to get the data written by the OTF C Logic + * @param zigZag Operate loop in zig zag mode + */ + public OTFLoop(String channelPrefix, String server, String share, String smbShare, boolean zigZag){ + + // Initialize connection to the OTF records + try { + this.obean = new OTFBean(); + ChannelBeanFactory.getFactory().createChannelBeans(obean, channelPrefix); + } catch (CAException e) { + throw new IllegalArgumentException("Unable to connect to the OTF channels",e); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to connect to the OTF channels",e); + } + + // Store loop configuration + this.server = server; + this.share = share; + this.smbShare = smbShare; + this.zigZag = zigZag; + + // Initialize lists used by the loop + this.preActions = new ArrayList(); + this.postActions = new ArrayList(); + this.sensors = new ArrayList(); + + this.dataQueue = new LinkedBlockingQueue(2000); + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#execute() + */ + @Override + public void execute() throws InterruptedException { + + // Execute pre actions + for(Action action: preActions){ + action.execute(); + } + + // Start scan + obean.start(); + + // Wait for end of scan + obean.waitUntilStopped(); + + // Read data from file + collectData(); + + // Execute post actions + for(Action action: postActions){ + action.execute(); + } + + + // Issue end of loop control message + dataQueue.put(new EndOfStreamMessage(dataGroup)); + + // Increase execution count + executionCount++; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#abort() + */ + @Override + public void abort() { + // Abort otf scan logic + obean.abort(); + + abort=true; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#prepare() + */ + @Override + public void prepare() { + executionCount = 0; + + // Set abort flag to false + abort=false; + + // Check whether actor is set for the loop + if(actor == null){ + throw new IllegalStateException("No actor specified for this loop"); + } + + // list with all monitored channels + List monitoredChannels = new ArrayList(); + dataIndexes = new ArrayList(); + int channelCount =0; + for(Sensor s: sensors){ + if(s instanceof OTFNamedChannelSensor){ + // Monitored channel + OTFNamedChannelSensor so = (OTFNamedChannelSensor) s; + if(channelCount>=numberOfMonitoredChannels){ + throw new IllegalArgumentException("Only up to "+numberOfMonitoredChannels+" channels can be monitored by OTF"); + } + + monitoredChannels.add(so.getName()); + dataIndexes.add(2+numberOfScalerChannels+channelCount); + + + channelCount++; + } + else if (s instanceof OTFScalerChannelSensor){ + OTFScalerChannelSensor so = (OTFScalerChannelSensor) s; + if(so.getIndex()>=numberOfScalerChannels){ + throw new IllegalArgumentException("Scaler index must be between 0<=index<"+numberOfScalerChannels); + } + dataIndexes.add(2+so.getIndex()); // scalers follow directly after the readback + } + else if (s instanceof MillisecondTimestampSensor){ + dataIndexes.add(2+numberOfScalerChannels+numberOfMonitoredChannels); + } + else if (s instanceof OTFReadbackSensor){ + dataIndexes.add(1); + } + else{ + throw new IllegalArgumentException("Sensor type "+s.getClass()+" is not supported by this loop"); + } + } + + // Set OTF parameters + try{ + obean.resetToDefaults(); + + // Set actor properties + obean.setMotor(actor.getName()); + obean.waitUntilMotorOk(timeout); + + obean.setBegin(actor.getStart()); + obean.setEnd(actor.getEnd()); + obean.setStepSize(actor.getStepSize()); + obean.setIntegrationTime(actor.getIntegrationTime()); + + // Override encoder if specified + if(actor.getReadback()!=null){ + obean.setUseEncoder(true); + obean.setEncoder(actor.getReadback()); + obean.waitUntilEncoderOk(timeout); + } + + // Set user backlash + obean.setUserBacklash(actor.getAdditionalBacklash()); + + + // NFS settings + obean.setNfsServer(server); + obean.setNfsShare(share); + + obean.setFileNameGeneration(true); + obean.setAppendFile(false); + obean.setZigZag(zigZag); // Set ZigZag because there might be iterations + obean.setFileNameFormat("%06d"); // Force an update of the filename/counter by setting file format twice with different values + obean.setFileNameFormat("%06d.txt"); + + + + // Set monitored channels + obean.setMonitoredChannels(monitoredChannels.toArray(new String[monitoredChannels.size()])); + } + catch(CAException e){ + throw new RuntimeException("Unable to set OTF configuration parameters",e); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to set OTF configuration parameters",e); + } + + + // Cleanup temporary directory + try{ + SmbFile tmpDir = new SmbFile(smbShare); + if( !tmpDir.exists() || !tmpDir.isDirectory() ){ + throw new RuntimeException("Cannot access OTF temporary directory "+tmpDir.getCanonicalPath()); + } + SmbFile[] files = tmpDir.listFiles(); + + for(int i=0;i getPreActions() { + return preActions; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#getPostActions() + */ + @Override + public List getPostActions() { + return postActions; + } + + /** + * The structure of the data message depends on the sensors registered at this loop + * at the time this method is called. + * @return the data queue and the metadata of the data messages + */ + public DataQueue getDataQueue() { + DataMessageMetadata m = new DataMessageMetadata(); + + // Build up data message metadata based on the sensors currently registered. + for(Sensor s: sensors){ + m.getComponents().add(new ComponentMetadata(s.getId())); + } + return new DataQueue(dataQueue, m); + } + + /** + * Collect data written by the OTFScan logic + * @param dataSet + * @param tmpFile + */ + private void collectData() { + try{ + final int timestampIndex = 2+numberOfScalerChannels+numberOfMonitoredChannels; + + SmbFile tmpFile = new SmbFile(smbShare, String.format("%06d.txt", executionCount)); + logger.fine("Collect data from "+tmpFile.getCanonicalPath()); + + SmbFile lockfile = new SmbFile(tmpFile.getCanonicalPath() + ".lock"); + + logger.fine("Wait until file is written [lock file: "+lockfile.getCanonicalPath()+"]"); + // Wait until file is created + while ((!tmpFile.exists()) || lockfile.exists()) { + Thread.sleep(500); + if(abort){ + // If abort is issued while waiting for data immediately return without + // trying to read the data + return; + } + } + + logger.fine("Read file " + tmpFile.getCanonicalPath()); + InputStreamReader inreader = new InputStreamReader(tmpFile.getInputStream()); + BufferedReader in = new BufferedReader(inreader); + String line; + boolean firstline = true; + while (true) { + line = in.readLine(); + if (line == null) { + break; + } else { + if(line.matches("^\\[.*")){ + // Skip header lines + } + else{ + if(firstline){ + firstline=false; + continue; + } + + DataMessage message = new DataMessage(); + // Add data to dataset + String[] tokens = line.split("\t"); + + for(Integer i: dataIndexes){ + try{ + if(i == timestampIndex) { + // Calculate time in milliseconds + Double seconds = new Double(tokens[i]); + Double nanoseconds = new Double(tokens[i+1]); + + Double v = seconds*1000+Math.floor(nanoseconds*0.000001); + message.getData().add(v); + } + else { + + message.getData().add(new Double(tokens[i])); + } + } + catch(NumberFormatException e){ + logger.warning("Cannot parse component ["+tokens[i]+"] from source file - will add 0 for this component"); + message.getData().add(new Double(0)); + } + } + dataQueue.put(message); + } + } + } + in.close(); + inreader.close(); + } catch(InterruptedException e){ + throw new RuntimeException("An interrupt occured while waiting for the file to show up",e); + } catch (IOException e) { + throw new RuntimeException("An IO Exception occured while reading the OTF data file",e); + } + } + + + // Getter functions for variable parts + + + + /** + * @return the sensors + */ + public List getSensors() { + return sensors; + } + + /** + * @return the actor + */ + public OTFActuator getActor() { + return actor; + } + + /** + * @param actor the actor to set + */ + public void setActor(OTFActuator actor) { + this.actor = actor; + } + + /** + * @return the dataGroup + */ + public boolean isDataGroup() { + return dataGroup; + } + + /** + * @param dataGroup the dataGroup to set + */ + public void setDataGroup(boolean dataGroup) { + this.dataGroup = dataGroup; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/CrlogicChannelsTemplate.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/CrlogicChannelsTemplate.java new file mode 100644 index 0000000..107dec1 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/CrlogicChannelsTemplate.java @@ -0,0 +1,157 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.loops.cr; + +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.annotation.CaChannel; + +/** + * @author ebner + * + */ +public class CrlogicChannelsTemplate { + + /** + * Status of the OTFSCAN IOC logic + */ + public enum Status { SETUP, INACTIVE, INITIALIZE, ACTIVE, STOP, FAULT, ERROR }; + + /** + * Ticks per second - IOC setting + * ATTENTION - This field must only be set bu the IOC - ATTENTION + */ + @CaChannel(type=Integer.class, name =":TPS") + private ChannelBean ticksPerSecond; + + /** + * Status of the OTFSCAN IOC logic + */ + @CaChannel(type=String.class, name =":STATUS") + private ChannelBean status; + + /** + * Message from the OTFSCAN IOC logic + */ + @CaChannel(type=String.class, name =":MSG") + private ChannelBean message; + + /** + * IOC ticks between data acquisition interrupts + */ + @CaChannel(type=Integer.class, name =":TBINT") + private ChannelBean ticksBetweenInterrupts; + + /** + * Name or ip address of the NFS server to save the data to + * (depending on the IOC setup) + */ + @CaChannel(type=String.class, name =":NFSSE") + private ChannelBean nfsServer; + + /** + * Name of the NFS share on the NFS server + */ + @CaChannel(type=String.class, name =":NFSSH") + private ChannelBean nfsShare; + + /** + * Name of the data file + */ + @CaChannel(type=String.class, name =":DFNAM") + private ChannelBean dataFile; + + /** + * Flag to identify whether the data file should be appended + */ + @CaChannel(type=Boolean.class, name =":FAPPE") + private ChannelBean appendFile; + + + /** + * Readout resources + */ + @CaChannel(type=String[].class, name =":RRES") + private ChannelBean readoutResources; + + + /** + * @return the ticksPerSecond + */ + public ChannelBean getTicksPerSecond() { + return ticksPerSecond; + } + + /** + * @return the status + */ + public ChannelBean getStatus() { + return status; + } + + /** + * @return the message + */ + public ChannelBean getMessage() { + return message; + } + + /** + * @return the ticksBetweenInterrupts + */ + public ChannelBean getTicksBetweenInterrupts() { + return ticksBetweenInterrupts; + } + + /** + * @return the nfsServer + */ + public ChannelBean getNfsServer() { + return nfsServer; + } + + /** + * @return the nfsShare + */ + public ChannelBean getNfsShare() { + return nfsShare; + } + + /** + * @return the dataFile + */ + public ChannelBean getDataFile() { + return dataFile; + } + + /** + * @return the appendFile + */ + public ChannelBean getAppendFile() { + return appendFile; + } + + /** + * @return the readoutResources + */ + public ChannelBean getReadoutResources() { + return readoutResources; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/CrlogicDeltaDataFilter.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/CrlogicDeltaDataFilter.java new file mode 100644 index 0000000..08b4f7c --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/CrlogicDeltaDataFilter.java @@ -0,0 +1,47 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.loops.cr; + +/** + * @author ebner + * + */ +public class CrlogicDeltaDataFilter { + + private Double lastValue = null; + + public void reset(){ + lastValue = null; + } + + public Double delta(Double value){ + + Double lvalue = lastValue; + lastValue = value; + + if(lvalue==null){ + return Double.NaN; + } + else{ + // Return delta + return(value-lvalue); + } + } +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/CrlogicLoop.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/CrlogicLoop.java new file mode 100644 index 0000000..a84ff8e --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/CrlogicLoop.java @@ -0,0 +1,824 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.loops.cr; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.Semaphore; +import java.util.logging.Logger; + +import jcifs.smb.SmbFile; +import gov.aps.jca.CAException; +import ch.psi.fda.core.Action; +import ch.psi.fda.core.ActionLoop; +import ch.psi.fda.core.Sensor; +import ch.psi.fda.core.actors.OTFActuator; +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; +import ch.psi.fda.core.sensors.MillisecondTimestampSensor; +import ch.psi.fda.core.sensors.OTFNamedChannelSensor; +import ch.psi.fda.core.sensors.OTFScalerChannelSensor; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * @author ebner + * + * While using Crlogic the IOC system clock rate should/must be set to 1000 (default 60) + * + * sysClkRateSet 1000 + * + */ +public class CrlogicLoop implements ActionLoop { + + // Get Logger + private static final Logger logger = Logger.getLogger(CrlogicLoop.class.getName()); + + /** + * Flag to indicate whether the data of this loop will be grouped + * According to this flag the dataGroup flag in EndOfStream will be set. + */ + private boolean dataGroup = false; + + // Constants + + /** + * Default timeout (in milliseconds) for wait operations + */ + private long startStopTimeout = 8000; + + /** + * Name of the NFS server to place the data of the OTF logic + */ + private final String server; + /** + * Share on the NFS server to put the OTF data on to + */ + private final String share; + /** + * SMB share to access the data written by the OTF C logic + */ + private final String smbShare; + + /** + * Flag whether the actor of this loop should move in zig zag mode + */ + private final boolean zigZag; + + boolean useReadback; + boolean useEncoder; + + /** + * List of actions that are executed at the beginning of the loop. + */ + private List preActions; + /** + * List of actions that are executed at the end of the loop. + */ + private List postActions; + + /** + * Prefix for the CRLOGIC channels + */ + private String prefix; + + private CrlogicChannelsTemplate template; + private MotorChannelsTemplate motortemplate; + + /** + * Semaphore to ensure that data is read in correct sequence + */ + private Semaphore semaphore = new Semaphore(1); + + /** + * Special OTF Actuator + */ + private OTFActuator actuator = null; + + /** + * List of sensors of this loop + */ + private List sensors; + + private List readoutResources; + private Map scalerIndices; + private CrlogicRangeDataFilter crlogicDataFilter; + + /** + * Data queue sensor data is posted to. A message consists of a list of data objects + * that are read out of the sensors of this loop. + */ + private BlockingQueue dataQueue; + + /** + * Abort status + */ + private boolean abort = false; + private boolean abortForce = false; + private Thread executionThread = null; + + + public CrlogicLoop(String prefix, String server, String share, String smbShare, boolean zigZag){ + this.prefix = prefix; + this.server = server; + this.share = share; + this.smbShare = smbShare; + this.zigZag = zigZag; + + // Initialize lists used by the loop + this.preActions = new ArrayList(); + this.postActions = new ArrayList(); + this.sensors = new ArrayList(); + this.readoutResources = new ArrayList(); + this.scalerIndices = new HashMap(); + + this.crlogicDataFilter = new CrlogicRangeDataFilter(); + this.dataQueue = new LinkedBlockingQueue(2000); + } + + + /** + * Collect data from share + * @param tmpFileName Name of the temporary file + * @throws InterruptedException + * @throws IOException + */ + private void collectData(String tmpDir, String tmpFileName) throws InterruptedException, IOException { + semaphore.acquire(); + if (tmpDir.startsWith("smb:")) { + + SmbFile tmpFile = new SmbFile(tmpDir, tmpFileName); + logger.info("Collect data from " + tmpFile.getCanonicalPath()); + + SmbFile lockfile = new SmbFile(tmpFile.getCanonicalPath() + ".lock"); + + logger.info("Wait until file is written [lock file: " + lockfile.getCanonicalPath() + "]"); + // Wait until file is created + while ((!tmpFile.exists()) || lockfile.exists()) { + Thread.sleep(100); + if(abort){ + // If abort is issued while waiting for data immediately return without + // trying to read the data + return; + } + } + + InputStreamReader inreader = new InputStreamReader(tmpFile.getInputStream()); + BufferedReader in = new BufferedReader(inreader); + String line; + boolean firstline = true; + + boolean wasInRangeBefore = false; + boolean discardAnyway = false; + + while (true) { + line = in.readLine(); + if (line == null) { + break; + } else { + // if(line.matches("^\\[.*")){ + if (line.matches("^ *#.*")) { + // Skip header/comment lines +// logger.info("HEADER: " + line); + } else { + if (firstline) { + firstline = false; + continue; + } + +// logger.info(line); + + // Write into queue + DataMessage message = new DataMessage(); + String[] tokens = line.split("\t"); + boolean use = true; + + for(int i=0;imotorHighLimit || startmotorHighLimit || end0){ + double maxIntegrationTime = stepSize/motorMinSpeed; + if(integrationTime>maxIntegrationTime){ + logger.info("Integration time is too big"); + // Integration time is too big + throw new IllegalArgumentException("Integration time is too big"); + } + } + double motorMaxSpeed = motortemplate.getVelocity().getValue(); + double minIntegrationTime = Math.min( (stepSize/motorMaxSpeed), ((double)minimumTicks/(double)tps) ); + if(integrationTime getPreActions() { + return preActions; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#getPostActions() + */ + @Override + public List getPostActions() { + return postActions; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#isDataGroup() + */ + @Override + public boolean isDataGroup() { + // TODO Auto-generated method stub + return dataGroup; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#setDataGroup(boolean) + */ + @Override + public void setDataGroup(boolean dataGroup) { + this.dataGroup = dataGroup; + } + + /** + * @return the sensors + */ + public List getSensors() { + return sensors; + } + + /** + * @return the actor + */ + public OTFActuator getActor() { + return actuator; + } + + /** + * @param actor the actor to set + */ + public void setActor(OTFActuator actor) { + this.actuator = actor; + } + + /** + * The structure of the data message depends on the sensors registered at this loop + * at the time this method is called. + * @return the data queue and the metadata of the data messages + */ + public DataQueue getDataQueue() { + DataMessageMetadata m = new DataMessageMetadata(); + + // Build up data message metadata based on the sensors currently registered. + m.getComponents().add(new ComponentMetadata(actuator.getId())); + for(Sensor s: sensors){ + m.getComponents().add(new ComponentMetadata(s.getId())); + } + return new DataQueue(dataQueue, m); + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/CrlogicRangeDataFilter.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/CrlogicRangeDataFilter.java new file mode 100644 index 0000000..50606d9 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/CrlogicRangeDataFilter.java @@ -0,0 +1,274 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.loops.cr; + +/** + * @author ebner + * + */ +public class CrlogicRangeDataFilter { + + private double motorResolution = 1; + private int motorDirection = 1; + private double motorOffset = 0; + private double motorReadbackResolution = 1; + private double motorEncoderResolution = 1; + + private double encoderOffset = 0; + private double encoderResolution = 1; + private int encoderDirection = 1; + + private double start = 0; + private double end = 0; + private boolean positive = true; + + /** + * Marker whether the value was equal + */ + private boolean wasEqualBefore = false; + + /** + * @return the motorResolution + */ + public double getMotorResolution() { + return motorResolution; + } + + /** + * @param motorResolution the motorResolution to set + */ + public void setMotorResolution(double motorResolution) { + this.motorResolution = motorResolution; + } + + /** + * @return the motorDirection + */ + public int getMotorDirection() { + return motorDirection; + } + + /** + * @param motorDirection the motorDirection to set + */ + public void setMotorDirection(int motorDirection) { + this.motorDirection = motorDirection; + } + + /** + * @return the motorOffset + */ + public double getMotorOffset() { + return motorOffset; + } + + /** + * @param motorOffset the motorOffset to set + */ + public void setMotorOffset(double motorOffset) { + this.motorOffset = motorOffset; + } + + /** + * @return the motorReadbackResolution + */ + public double getMotorReadbackResolution() { + return motorReadbackResolution; + } + + /** + * @param motorReadbackResolution the motorReadbackResolution to set + */ + public void setMotorReadbackResolution(double motorReadbackResolution) { + this.motorReadbackResolution = motorReadbackResolution; + } + + /** + * @return the motorEncoderResolution + */ + public double getMotorEncoderResolution() { + return motorEncoderResolution; + } + + /** + * @param motorEncoderResolution the motorEncoderResolution to set + */ + public void setMotorEncoderResolution(double motorEncoderResolution) { + this.motorEncoderResolution = motorEncoderResolution; + } + + /** + * @return the encoderOffset + */ + public double getEncoderOffset() { + return encoderOffset; + } + + /** + * @param encoderOffset the encoderOffset to set + */ + public void setEncoderOffset(double encoderOffset) { + this.encoderOffset = encoderOffset; + } + + /** + * @return the encoderResolution + */ + public double getEncoderResolution() { + return encoderResolution; + } + + /** + * @param encoderResolution the encoderResolution to set + */ + public void setEncoderResolution(double encoderResolution) { + this.encoderResolution = encoderResolution; + } + + /** + * @return the encoderDirection + */ + public int getEncoderDirection() { + return encoderDirection; + } + + /** + * @param encoderDirection the encoderDirection to set + */ + public void setEncoderDirection(int encoderDirection) { + this.encoderDirection = encoderDirection; + } + + + /** + * @return the start + */ + public double getStart() { + return start; + } + + /** + * @param start the start to set + */ + public void setStart(double start) { + this.start = start; + if(start<=end){ + positive=true; + } + else{ + positive=false; + } + } + + /** + * @return the end + */ + public double getEnd() { + return end; + } + + /** + * @param end the end to set + */ + public void setEnd(double end) { + this.end = end; + if(end>=start){ + positive=true; + } + else{ + positive=false; + } + } + + /** + * Calculate real position + * @param raw + * @return + */ + public double calculatePositionMotor(double raw){ + return(((raw * motorResolution * motorReadbackResolution)/motorDirection)+motorOffset); + } + + /** + * Calculate real motor position using the readback link + * @param raw + * @return + */ + public double calculatePositionMotorUseReadback(double raw){ + return((((raw - encoderOffset) * encoderResolution * encoderDirection * motorReadbackResolution) / motorDirection) + motorOffset); + } + + /** + * Calculate real motor position using encoder + * @param raw + * @return + */ + public double calculatePositionMotorUseEncoder(double raw){ + return(raw * motorEncoderResolution * motorReadbackResolution/motorDirection + motorOffset); + } + + /** + * Filter whether value is with the range + * @param value + * @return + */ + public boolean filter(Double value){ + if(positive){ + if(start<=value && value<=end){ + + // If motor is very accurete and user backlash==null it might be that value is exactly + // the end value. To prevent that unnecessary data is captured execute this check + if(wasEqualBefore){ + wasEqualBefore=false; // Reset flag + return false; + } + + // Check whether was equal + if(value==end){ + wasEqualBefore=true; + } + + return true; + } + else{ + return false; + } + } + else{ + if(end<=value && value <=start){ + + if(wasEqualBefore){ + wasEqualBefore=false; // Reset flag + return false; + } + + // Check whether was equal + if(value==start){ + wasEqualBefore=true; + } + + return true; + } + else{ + return false; + } + } + } +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/EncoderChannelsTemplate.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/EncoderChannelsTemplate.java new file mode 100644 index 0000000..ef8cb63 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/EncoderChannelsTemplate.java @@ -0,0 +1,73 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.loops.cr; + +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.annotation.CaChannel; + +/** + * @author ebner + * + */ +public class EncoderChannelsTemplate { + + /** + * Resolution - $(P)$(E)_SCL + */ + @CaChannel(type=Double.class, name="_SCL") + private ChannelBean resolution; + + /** + * Offset - $(P)$(E)_OFF + */ + @CaChannel(type=Double.class, name ="_OFF") + private ChannelBean offset; + + /** + * Direction - $(P)$(E)_DIR + */ + public enum Direction {Negative, Positive}; + @CaChannel(type=Integer.class, name ="_DIR") + private ChannelBean direction; + + + /** + * @return the resolution + */ + public ChannelBean getResolution() { + return resolution; + } + + /** + * @return the offset + */ + public ChannelBean getOffset() { + return offset; + } + + /** + * @return the direction + */ + public ChannelBean getDirection() { + return direction; + } + + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/MotorChannelsTemplate.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/MotorChannelsTemplate.java new file mode 100644 index 0000000..c6c47da --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/MotorChannelsTemplate.java @@ -0,0 +1,652 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.loops.cr; + +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.annotation.CaChannel; + +/** + * @author ebner + * + */ +public class MotorChannelsTemplate { + + public enum Type {SOFT_CHANNEL, MOTOR_SIMULATION, OMS_VME58, OMS_MAXv}; + + /** + * ## Drive section ## + * # User coordinates # + */ + + /** + * .HLM High limit - double + */ + @CaChannel(type=Double.class, name =".HLM") + private ChannelBean highLimit; + + /** + * .LLM Low limit - double + */ + @CaChannel(type=Double.class, name =".LLM") + private ChannelBean lowLimit; + + /** + * .RBV Readback value - double + */ + @CaChannel(type=Double.class, name =".RBV", monitor=true) + private ChannelBean readbackValue; + + /** + * .VAL Set value - double + */ + @CaChannel(type=Double.class, name =".VAL", monitor=true) + private ChannelBean setValue; + + /** + * .RLV Relative move value - double + */ + @CaChannel(type=Double.class, name =".RLV") + private ChannelBean relativeMoveValue; + + /** + * .TWV Teak value - double + */ + @CaChannel(type=Double.class, name =".TWV") + private ChannelBean tweakValue; + + /** + * .TWR Tweak reverse - move left - int + */ + @CaChannel(type=Integer.class, name =".TWR") + private ChannelBean tweakReverse; + + /** + * .TWF Tweak forward - move right - int + */ + @CaChannel(type=Integer.class, name =".TWF") + private ChannelBean tweakForward; + + /** + * .JOGR Jog reverse - int + */ + @CaChannel(type=Integer.class, name =".JOGR") + private ChannelBean jogReverse; + + /** + * .JOGF Jog forward - int + */ + @CaChannel(type=Integer.class, name =".JOGF") + private ChannelBean jogForward; + + /** + * .HOMR Home reverse - int + */ + @CaChannel(type=Integer.class, name =".HOMR") + private ChannelBean homeReverse; + + /** + * .HOMF Home forward - int + */ + @CaChannel(type=Integer.class, name =".HOMF") + private ChannelBean homeForward; + + /** + * .EGU Engineering unit - String + */ + @CaChannel(type=String.class, name =".EGU") + private ChannelBean engineeringUnit; + + /** + * .DTYP Type - String (e.g. "OMS MAXv") see enum Type + */ + @CaChannel(type=Integer.class, name =".DTYP") + private ChannelBean type; + + /** + * .DESC Description - String + */ + @CaChannel(type=String.class, name =".DESC") + private ChannelBean description; + + + /** + * # Dial coordinates # + */ + + /** + * .DHLM Dial high limit - double + */ + @CaChannel(type=Double.class, name =".DHLM") + private ChannelBean dialHighLimit; + + /** + * .DLLM Dial low limit - double + */ + @CaChannel(type=Double.class, name =".DLLM") + private ChannelBean dialLowLimit; + + /** + * .DRBV Dial readback value - double + */ + @CaChannel(type=Double.class, name =".DRBV", monitor=true) + private ChannelBean dialReadbackValue; + + /** + * .DVAL Dial set value - double + */ + @CaChannel(type=Double.class, name =".DVAL", monitor=true) + private ChannelBean dialSetValue; + + /** + * .RVAL Raw value - int + */ + @CaChannel(type=Integer.class, name =".RVAL", monitor=true) + private ChannelBean rawValue; + + /** + * .RRBV Raw readback value - int + */ + @CaChannel(type=Integer.class, name =".RRBV", monitor=true) + private ChannelBean rawReadbackValue; + + /** + * .SPMG Stop/Pause/Move/Go - (0:"Stop", 1:"Pause", 2:"Move", 3:"Go") - 3 + */ + public enum Commands { Stop, Pause, Move, Go }; + @CaChannel(type=Integer.class, name =".SPMG") + private ChannelBean command; + + + /** + * ## Calibration section ## + */ + + /** + * .SET Set/Use Switch - (0:"Use", 1:"Set") - 0 + */ + public enum Calibration {Use, Set}; + @CaChannel(type=Integer.class, name =".SET") + private ChannelBean calibration; + + /** + * .OFF User offset (EGU) - double + */ + @CaChannel(type=Double.class, name =".OFF") + private ChannelBean offset; + + /** + * .FOFF Offset-Freeze Switch - (0:"Variable", 1:"Frozen") - 1 + */ + public enum OffsetMode {Variable, Frozen}; + @CaChannel(type=Integer.class, name =".FOFF") + private ChannelBean offsetMode; + + /** + * .DIR User direction - (0:"Pos", 1:"Neg") + */ + public enum Direction {Positive, Negative}; + @CaChannel(type=Integer.class, name =".DIR") + private ChannelBean direction; + + + /** + * ## Dynamics ## + */ + + /** + * .VELO Velocity (EGU/s) - double + */ + @CaChannel(type=Double.class, name =".VELO") + private ChannelBean velocity; + + /** + * .BVEL Backlash velocity (EGU/s) - double + */ + @CaChannel(type=Double.class, name =".BVEL") + private ChannelBean backlashVelocity; + + /** + * .VBAS Base speed (EGU/s) - double + */ + @CaChannel(type=Double.class, name =".VBAS") + private ChannelBean baseSpeed; + + /** + * .ACCL Acceleration time / seconds to velocity - double + */ + @CaChannel(type=Double.class, name =".ACCL") + private ChannelBean accelerationTime; + + /** + * .BACC Backlash acceleration time / seconds to velocity - double + */ + @CaChannel(type=Double.class, name =".BACC") + private ChannelBean backlashAccelerationTime; + + /** + * .BDST Backlash distance (EGU) - double + */ + @CaChannel(type=Double.class, name =".BDST") + private ChannelBean backlashDistance; + + /** + * .FRAC Move fraction - double + */ + @CaChannel(type=Double.class, name =".FRAC") + private ChannelBean moveFracion; + + + /** + * ## Resolution ## + */ + + /** + * .MRES Motor resolution - double + */ + @CaChannel(type=Double.class, name =".MRES") + private ChannelBean motorResolution; + + /** + * .ERES Encoder resolution - double + */ + @CaChannel(type=Double.class, name =".ERES") + private ChannelBean encoderResolution; + + /** + * .RRES Readback resolution - double + */ + @CaChannel(type=Double.class, name =".RRES") + private ChannelBean readbackResolution; + + /** + * .RDBD Retry deadband (EGU) - double + */ + @CaChannel(type=Double.class, name =".RDBD") + private ChannelBean retryDeadband; + + /** + * .RTRY Max retry count - int + */ + @CaChannel(type=Integer.class, name =".RTRY") + private ChannelBean maxRetryCount; + + /** + * .RCNT Retry count - int + */ + @CaChannel(type=Integer.class, name =".RCNT", monitor=true) + private ChannelBean retryCount; + + /** + * .UEIP Use encoder (if present) - (0:"No", 1:"Yes") + */ + @CaChannel(type=Boolean.class, name =".UEIP") + private ChannelBean useEncoder; + + /** + * .URIP Use readback link (if present) - (0:"No", 1:"Yes") + */ + @CaChannel(type=Boolean.class, name =".URIP") + private ChannelBean useReadback; + + /** + * .DLY Readback delay (s) - double + */ + @CaChannel(type=Double.class, name =".DLY") + private ChannelBean readbackDelay; + + /** + * .RDBL Readback link - String + */ + @CaChannel(type=String.class, name =".RDBL") + private ChannelBean readbackLink; + + /** + * .OMSL Output mode select - (0:"supervisory", 1:"closed_loop") + */ + public enum OutputMode {Supervisory, Closed_Loop}; + @CaChannel(type=Integer.class, name =".OMSL") + private ChannelBean outputMode; + + /** + * ## Status ## + */ + + /** + * .DMOV Done move - int + */ + @CaChannel(type=Boolean.class, name =".DMOV", monitor=true) + private ChannelBean moveDone; + + /** + * @return the highLimit + */ + public ChannelBean getHighLimit() { + return highLimit; + } + + /** + * @return the lowLimit + */ + public ChannelBean getLowLimit() { + return lowLimit; + } + + /** + * @return the readbackValue + */ + public ChannelBean getReadbackValue() { + return readbackValue; + } + + /** + * @return the setValue + */ + public ChannelBean getSetValue() { + return setValue; + } + + /** + * @return the relativeMoveValue + */ + public ChannelBean getRelativeMoveValue() { + return relativeMoveValue; + } + + /** + * @return the tweakValue + */ + public ChannelBean getTweakValue() { + return tweakValue; + } + + /** + * @return the tweakReverse + */ + public ChannelBean getTweakReverse() { + return tweakReverse; + } + + /** + * @return the tweakForward + */ + public ChannelBean getTweakForward() { + return tweakForward; + } + + /** + * @return the jogReverse + */ + public ChannelBean getJogReverse() { + return jogReverse; + } + + /** + * @return the jogForward + */ + public ChannelBean getJogForward() { + return jogForward; + } + + /** + * @return the homeReverse + */ + public ChannelBean getHomeReverse() { + return homeReverse; + } + + /** + * @return the homeForward + */ + public ChannelBean getHomeForward() { + return homeForward; + } + + /** + * @return the engineeringUnit + */ + public ChannelBean getEngineeringUnit() { + return engineeringUnit; + } + + /** + * @return the type + */ + public ChannelBean getType() { + return type; + } + + /** + * @return the description + */ + public ChannelBean getDescription() { + return description; + } + + /** + * @return the dialHighLimit + */ + public ChannelBean getDialHighLimit() { + return dialHighLimit; + } + + /** + * @return the dialLowLimit + */ + public ChannelBean getDialLowLimit() { + return dialLowLimit; + } + + /** + * @return the dialReadbackValue + */ + public ChannelBean getDialReadbackValue() { + return dialReadbackValue; + } + + /** + * @return the dialSetValue + */ + public ChannelBean getDialSetValue() { + return dialSetValue; + } + + /** + * @return the rawValue + */ + public ChannelBean getRawValue() { + return rawValue; + } + + /** + * @return the rawReadbackValue + */ + public ChannelBean getRawReadbackValue() { + return rawReadbackValue; + } + + /** + * @return the command + */ + public ChannelBean getCommand() { + return command; + } + + /** + * @return the calibration + */ + public ChannelBean getCalibration() { + return calibration; + } + + /** + * @return the userOffset + */ + public ChannelBean getOffset() { + return offset; + } + + /** + * @return the offsetMode + */ + public ChannelBean getOffsetMode() { + return offsetMode; + } + + /** + * @return the direction + */ + public ChannelBean getDirection() { + return direction; + } + + /** + * @return the velocity + */ + public ChannelBean getVelocity() { + return velocity; + } + + /** + * @return the backlashVelocity + */ + public ChannelBean getBacklashVelocity() { + return backlashVelocity; + } + + /** + * @return the baseSpeed + */ + public ChannelBean getBaseSpeed() { + return baseSpeed; + } + + /** + * @return the accelerationTime + */ + public ChannelBean getAccelerationTime() { + return accelerationTime; + } + + /** + * @return the backlashAccelerationTime + */ + public ChannelBean getBacklashAccelerationTime() { + return backlashAccelerationTime; + } + + /** + * @return the backlashDistance + */ + public ChannelBean getBacklashDistance() { + return backlashDistance; + } + + /** + * @return the moveFracion + */ + public ChannelBean getMoveFracion() { + return moveFracion; + } + + /** + * @return the motorResolution + */ + public ChannelBean getMotorResolution() { + return motorResolution; + } + + /** + * @return the encoderResolution + */ + public ChannelBean getEncoderResolution() { + return encoderResolution; + } + + /** + * @return the readbackResolution + */ + public ChannelBean getReadbackResolution() { + return readbackResolution; + } + + /** + * @return the retryDeadband + */ + public ChannelBean getRetryDeadband() { + return retryDeadband; + } + + /** + * @return the maxRetryCount + */ + public ChannelBean getMaxRetryCount() { + return maxRetryCount; + } + + /** + * @return the retryCount + */ + public ChannelBean getRetryCount() { + return retryCount; + } + + /** + * @return the useEncoder + */ + public ChannelBean getUseEncoder() { + return useEncoder; + } + + /** + * @return the useReadback + */ + public ChannelBean getUseReadback() { + return useReadback; + } + + /** + * @return the readbackDelay + */ + public ChannelBean getReadbackDelay() { + return readbackDelay; + } + + /** + * @return the readbackLink + */ + public ChannelBean getReadbackLink() { + return readbackLink; + } + + /** + * @return the outputMode + */ + public ChannelBean getOutputMode() { + return outputMode; + } + + /** + * @return the moveDone + */ + public ChannelBean getMoveDone() { + return moveDone; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/ParallelCrlogic.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/ParallelCrlogic.java new file mode 100644 index 0000000..4baefab --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/ParallelCrlogic.java @@ -0,0 +1,265 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.loops.cr; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; + +import ch.psi.fda.core.Action; +import ch.psi.fda.core.ActionLoop; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.sensors.MillisecondTimestampSensor; + +/** + * @author ebner + * + */ +public class ParallelCrlogic implements ActionLoop { + + // Get Logger + private static final Logger logger = Logger.getLogger(ParallelCrlogic.class.getName()); + + /** + * Flag to indicate whether the data of this loop will be grouped + * According to this flag the dataGroup flag in EndOfStream will be set. + */ + private boolean dataGroup = false; + + private CrlogicLoop crlogic; + private ScrlogicLoop scrlogic; + + /** + * List of actions that are executed at the beginning of the loop. + */ + private List preActions; + /** + * List of actions that are executed at the end of the loop. + */ + private List postActions; + + private ParallelCrlogicStreamMerge merger; + + + public ParallelCrlogic(CrlogicLoop crlogic, ScrlogicLoop scrlogic){ + + if(crlogic==null){ + throw new IllegalArgumentException("No Crloop specified"); + } + if(scrlogic==null){ + throw new IllegalArgumentException("No Scrloop specified"); + } + + this.crlogic = crlogic; + // Add timestamp to sensor at the beginning of the sensor list as this is required for merging the data + // Timestamp will be at the second position of a message in the queue! + this.crlogic.getSensors().add(0, new MillisecondTimestampSensor("tmp_timestamp")); + this.scrlogic = scrlogic; + + // Initialize lists used by the loop + this.preActions = new ArrayList(); + this.postActions = new ArrayList(); + + this.merger = new ParallelCrlogicStreamMerge(crlogic.getDataQueue(), scrlogic.getDataQueue()); + } + + + /* (non-Javadoc) + * @see ch.psi.fda.logic.Logic#prepare() + */ + @Override + public void prepare() { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see ch.psi.fda.logic.Logic#execute() + */ + @Override + public void execute() throws InterruptedException { + + // Execute pre actions + for(Action action: preActions){ + action.execute(); + } + + + ExecutorService service = Executors.newFixedThreadPool(3); // 2 for the parallel data acquisition and 1 for the merger thread + final CyclicBarrier b = new CyclicBarrier(2); + + List> list = new ArrayList>(); + + // Start a thread for each logic + logger.info("Submit logic"); + Future f = service.submit(new Callable(){ + @Override + public Boolean call() throws Exception { + + // Prepare logic + crlogic.prepare(); + + // Ensure that really all parallel logics start in parallel + b.await(); + + // Execute the logic of this path + crlogic.execute(); + + // Stop scrlogic + scrlogic.abort(); + return true; + }}); + list.add(f); + + //Start a thread for the scrlogic + logger.info("Submit logic"); + f = service.submit(new Callable(){ + @Override + public Boolean call() throws Exception { + + // Prepare logic + scrlogic.prepare(); + + // Ensure that really all parallel logics start in parallel + b.await(); + + // Execute the logic of this path + scrlogic.execute(); + return true; + }}); + list.add(f); + + + // Start data merge thread + logger.info("Start data merge"); + f = service.submit(new Callable(){ + @Override + public Boolean call() throws Exception { + + merger.merge(); + return true; + }}); + list.add(f); + + + for(Future bf: list){ + // Wait for completion of the thread + try { + bf.get(); + } catch (ExecutionException e) { + // TODO Handle exception the correct way + e.printStackTrace(); + } + } + + // Wait until all threads have finished + service.shutdown(); + service.awaitTermination(1, TimeUnit.MINUTES); + + + // Execute post actions + for(Action action: postActions){ + action.execute(); + } + + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#abort() + */ + @Override + public void abort() { + // TODO Auto-generated method stub + + } + + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#destroy() + */ + @Override + public void destroy() { + crlogic.destroy(); + scrlogic.destroy(); + } + + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#cleanup() + */ + @Override + public void cleanup() { + // TODO Auto-generated method stub + + } + + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#getPreActions() + */ + @Override + public List getPreActions() { + return preActions; + } + + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#getPostActions() + */ + @Override + public List getPostActions() { + return postActions; + } + + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#isDataGroup() + */ + @Override + public boolean isDataGroup() { + return dataGroup; + } + + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#setDataGroup(boolean) + */ + @Override + public void setDataGroup(boolean dataGroup) { + this.dataGroup = dataGroup; + } + + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#getDataQueue() + */ + @Override + public DataQueue getDataQueue() { + return(merger.getDataQueue()); + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/ParallelCrlogicStreamMerge.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/ParallelCrlogicStreamMerge.java new file mode 100644 index 0000000..a9c49c3 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/ParallelCrlogicStreamMerge.java @@ -0,0 +1,160 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.loops.cr; + +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; + +/** + * @author ebner + * + */ +public class ParallelCrlogicStreamMerge { + + private BlockingQueue dataQueue; + + private DataQueue primaryQueue; + private DataQueue secondaryQueue; + + /** + * Class to merge two data streams into one. The secondary queues data is added to the primary queues data. + * The resulting queue therefor will hold the same amount of elements as the primary queue does. + * The datagroup flag set in the end of stream message will be set according to the flag set in the primary queue. + * + * @param pqueue Primary/master queue + * @param squeue Secondary queue + */ + public ParallelCrlogicStreamMerge(DataQueue pqueue, DataQueue squeue){ + this.primaryQueue = pqueue; + this.secondaryQueue = squeue; + + this.dataQueue = new LinkedBlockingQueue(); + } + + /** + * Merge the streams based on the timestamp + * @throws InterruptedException + */ + public void merge() throws InterruptedException{ + + // Actual data of the secondary queue + List currData = null; + + // Take first element of the primary queue (wait until message is available) + Message m = primaryQueue.getQueue().take(); + while(! (m instanceof EndOfStreamMessage)){ + + if(m instanceof DataMessage){ + + DataMessage dm = (DataMessage) m; + // Get and remove merge timestamp from the data of the message + Double timestamp = (Double) dm.getData().remove(1); + long milliseconds = (long) (timestamp*1000); + long nanoOffset = (long)((timestamp*1000-milliseconds)*1000000); + +// System.out.println("timestamp: "+new Date(milliseconds)+" "+milliseconds+" ."+nanoOffset); + + while(true){ + // Assumption: the secondary Queue holds at least the data up to the + // timestamp of the primary queue + Message mess = secondaryQueue.getQueue().peek(); + + if(mess == null){ + break; + } + + if(mess instanceof DataMessage){ + // Check whether timestamp of the next message is bigger than the timestamp of the + // message from the primary queue - if the timestamp is bigger do not take message out of the queue + DataMessage msCheck = (DataMessage) mess; + +// System.out.println("Mess: "+mess); + + long currMilliCheck = ((Double) msCheck.getData().get(0)).longValue(); + long currNanoCheck = ((Double) msCheck.getData().get(1)).longValue(); + + // Check + if(currMilliCheck>milliseconds || (currMilliCheck==milliseconds && currNanoCheck>nanoOffset)){ + break; + } + + DataMessage ms = (DataMessage) secondaryQueue.getQueue().take(); + +// System.out.println("Ms: "+ms); + + currData = ms.getData(); + // Remove timestamps + currData.remove(0); + currData.remove(0); + } + else{ + // No DataMessage + break; + } + } + + // Add data to primary data queue message and put it into the out queue +// System.out.println(currData); + dm.getData().addAll(currData); + + dataQueue.add(dm); + } + + + m = primaryQueue.getQueue().take(); + } + + // Add the end of stream message of the primary queue + dataQueue.add(m); + } + + + /** + * The structure of the data message depends on the sensors registered at this loop + * at the time this method is called. + * @return the data queue and the metadata of the data messages + */ + public DataQueue getDataQueue() { + DataMessageMetadata m = new DataMessageMetadata(); + + DataMessageMetadata pqm = primaryQueue.getDataMessageMetadata(); + m.getComponents().add(pqm.getComponents().get(0)); // add first component (this is the actuator) + // Skip the next component as this is the timestamp used to merge the data + for(int i=2;i. + * + */ + +package ch.psi.fda.core.loops.cr; + +import gov.aps.jca.CAException; +import gov.aps.jca.Monitor; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.Semaphore; +import java.util.logging.Logger; + +import ch.psi.fda.core.Action; +import ch.psi.fda.core.ActionLoop; +import ch.psi.fda.core.Sensor; +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; +import ch.psi.fda.core.sensors.ChannelAccessDoubleSensor; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.MonitorListenerDoubleTimestamp; + +/** + * @author ebner + * + * Assumptions: + * - The delay between the monitor writing the value to the monitor queue and the readout of all the queues + * is sufficient to prevent the situation that some monitors of events close to each other on different IOC's have + * not arrived yet. + * - The sequence of monitors fired for one channel is according to the sequence of the causes. No monitor package is + * overtaking an other package on the network. + * + * - No monitor events are lost on the network (while using monitors you cannot guarantee this) + * + * The data queue returned by this logic includes two items for the timestamp and nanoseconds offset. These two items are the + * first two items of a message + * The id's are: + * crTimestampMilliseconds + * crTimestampOffsetNanoseconds + */ +public class ScrlogicLoop implements ActionLoop { + + private static String ID_TIMESTAMP_MILLISECONDS = "crTimestampMilliseconds"; + private static String ID_TIMESTAMP_OFFSET_NANOSECONDS = "crTimestampOffsetNanoseconds"; + + // Get Logger + private static final Logger logger = Logger.getLogger(ScrlogicLoop.class.getName()); + + private static Semaphore semaphore = new Semaphore(0); + + /** + * Data queue sensor data is posted to. A message consists of a list of data objects + * that are read out of the sensors of this loop. + */ + private BlockingQueue dataQueue; + + private boolean dataGroup = false; + private List preActions; + private List postActions; + + private List sensors; + private List monitors; + + private List> queues; + + private Thread mergeThread = null; + + + public ScrlogicLoop(List sensors){ + queues = new ArrayList>(); + preActions = new ArrayList(); + postActions = new ArrayList(); + + monitors = new ArrayList(); + this.sensors = sensors; + + this.dataQueue = new LinkedBlockingQueue(); + } + + /** + * @return the queues + */ + public List> getQueues() { + return queues; + } + + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#execute() + */ + @Override + public void execute() throws InterruptedException { + try{ + // Attach monitors to the channels + for(Sensor sensor: sensors){ + if(sensor instanceof ChannelAccessDoubleSensor){ + ChannelAccessDoubleSensor s = (ChannelAccessDoubleSensor) sensor; + ChannelBean b = s.getChannel(); + // Create data queue for the channel + final BlockingQueue q = new LinkedBlockingQueue(); + queues.add(q); + + Monitor m = b.attachMonitor(new MonitorListenerDoubleTimestamp() { + + @Override + public void valueChanged(Double value, Date timestamp, long nanosecondsOffset) { + // Add values to channel queue + q.add(new TimestampedValue(value, timestamp.getTime(), nanosecondsOffset)); + // Increase semaphore count (used for merging thread of the queues) + semaphore.release(); + } + }); + monitors.add(m); + } + } + } + catch(CAException e){ + new RuntimeException("Unable to create monitor for channels",e); + } + + logger.info("Start data acquisition"); + + // Start merge thread + mergeThread = new Thread(new Runnable() { + + @Override + public void run() { + try { + merge(); + } catch (InterruptedException e) { + // Normal termination + } + } + }); + mergeThread.start(); + + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#abort() + */ + @Override + public void abort() { + // Actually this is stopping the logic + + // Remove monitors + try{ + for(int i=0;i b = s.getChannel(); + b.removeMonitor(monitors.get(i)); + } + } + } + catch(CAException e){ + new RuntimeException(e); + } + + // Stop merge thread + mergeThread.interrupt(); + + // Clear data queues + for(BlockingQueue q: queues){ + q.clear(); + } + + // Put end of stream to the queue + dataQueue.add(new EndOfStreamMessage(dataGroup)); + + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Action#destroy() + */ + @Override + public void destroy() { + // Destroy all sensors + for(Sensor s: sensors){ + s.destroy(); + } + sensors.clear(); + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#prepare() + */ + @Override + public void prepare() { + // do nothing + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#cleanup() + */ + @Override + public void cleanup() { + // Do nothing + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#getPreActions() + */ + @Override + public List getPreActions() { + return preActions; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#getPostActions() + */ + @Override + public List getPostActions() { + return postActions; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#isDataGroup() + */ + @Override + public boolean isDataGroup() { + return dataGroup; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.ActionLoop#setDataGroup(boolean) + */ + @Override + public void setDataGroup(boolean dataGroup) { + this.dataGroup = dataGroup; + } + + /** + * The structure of the data message depends on the sensors registered at this loop + * at the time this method is called. + * @return the data queue and the metadata of the data messages + */ + public DataQueue getDataQueue() { + DataMessageMetadata m = new DataMessageMetadata(); + + // Build up data message metadata based on the channels registered. + m.getComponents().add(new ComponentMetadata(ID_TIMESTAMP_MILLISECONDS)); + m.getComponents().add(new ComponentMetadata(ID_TIMESTAMP_OFFSET_NANOSECONDS)); + for(Sensor s: sensors){ + m.getComponents().add(new ComponentMetadata(s.getId())); + } + return new DataQueue(dataQueue, m); + } + + + /** + * Merge data collected by different monitor + * + * @throws InterruptedException + */ + private void merge() throws InterruptedException{ + + // Array to hold temporary channel values + TimestampedValue[] cvalues = new TimestampedValue[queues.size()]; + TimestampedValueComparator comparator = new TimestampedValueComparator(); + + // Oldest value written + TimestampedValue globalOldest = null; + List indexes = new ArrayList(); + + while(!Thread.currentThread().isInterrupted()){ + + semaphore.acquire(); + Thread.sleep(10); // Ensure that close by monitors have time to catch up / also ensure context switch + + // Oldest value of this run + TimestampedValue oldest = null; + // Queue index of the oldest value of this run + indexes.clear(); + + // Find oldest element in any of the queues + for(int i=0;i q = queues.get(i); + TimestampedValue ttcheck = q.peek(); + + if(ttcheck != null){ + if(oldest==null){ + // Update the oldest variable with current element + oldest = ttcheck; + indexes.clear(); + indexes.add(i); + } + else if(comparator.compare(ttcheck, oldest)<0){ // Check whether timestamp is less (older) than the current oldest timestamp. + oldest = ttcheck; + indexes.clear(); + indexes.add(i); + } + else if(comparator.compare(ttcheck, oldest) == 0){ + // SAME TIMESTAMP + indexes.add(i); + } + else{ + } + } + } + +// logger.info("Index: "+index+" Permits: "+semaphore.availablePermits()); +// System.out.println("indexes: "+indexes.size()); + + if(indexes.size()>0){ + long timestamp = 0l; + long nanoOffset =0l; + + for(Integer index: indexes){ + // Get next older value + cvalues[index] = queues.get(index).poll(); + + + if(globalOldest != null){ + if(comparator.compare(cvalues[index], globalOldest)>=0){ + // Update the global oldest variable + globalOldest = cvalues[index]; + timestamp = cvalues[index].getTimestamp(); + nanoOffset = cvalues[index].getNanosecondsOffset(); + } + else{ + // Monitors did not fire in sequence (an newer monitor overtook an older (from an other IOC)) + logger.warning("Timestamped value out of sequence - discard value !!!!"); + // Continue with next value ... + continue; + } + } + else{ + globalOldest = cvalues[index]; + } + } + + + DataMessage message = new DataMessage(); + + message.getData().add(new Double(timestamp)); + message.getData().add(new Double(nanoOffset)); + + for(int y=0;y. + * + */ + +package ch.psi.fda.core.loops.cr; + +/** + * @author ebner + * + */ +public class TimestampedValue { + + private Double value; + private long timestamp; + private long nanosecondsOffset; + + public TimestampedValue(Double value, long timestamp, long nanosecondsOffset){ + this.value = value; + this.timestamp = timestamp; + this.nanosecondsOffset = nanosecondsOffset; + } + + /** + * @return the value + */ + public Double getValue() { + return value; + } + + /** + * @param value the value to set + */ + public void setValue(Double value) { + this.value = value; + } + + /** + * @return the timestamp + */ + public long getTimestamp() { + return timestamp; + } + + /** + * @param timestamp the timestamp to set + */ + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + /** + * @return the nanosecondsOffset + */ + public long getNanosecondsOffset() { + return nanosecondsOffset; + } + + /** + * @param nanosecondsOffset the nanosecondsOffset to set + */ + public void setNanosecondsOffset(long nanosecondsOffset) { + this.nanosecondsOffset = nanosecondsOffset; + } + + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/TimestampedValueComparator.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/TimestampedValueComparator.java new file mode 100644 index 0000000..965b6e7 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/TimestampedValueComparator.java @@ -0,0 +1,45 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.loops.cr; + +import java.util.Comparator; + +/** + * @author ebner + * + */ +public class TimestampedValueComparator implements Comparator { + + @Override + public int compare(TimestampedValue o1, TimestampedValue o2) { + if (o1.getTimestamp() < o2.getTimestamp()) { + return -1; + } else if (o1.getTimestamp() > o2.getTimestamp()) { + return 1; + } else if (o1.getTimestamp() == o2.getTimestamp()) { + if (o1.getNanosecondsOffset() < o2.getNanosecondsOffset()) { + return -1; + } else if (o1.getNanosecondsOffset() > o2.getNanosecondsOffset()) { + return 1; + } + } + return 0; + } +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/VSC16ScalerChannelsTemplate.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/VSC16ScalerChannelsTemplate.java new file mode 100644 index 0000000..16e5713 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/loops/cr/VSC16ScalerChannelsTemplate.java @@ -0,0 +1,103 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.loops.cr; + +import java.util.List; + +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.annotation.CaChannel; + +/** + * @author ebner + * + */ +public class VSC16ScalerChannelsTemplate { + + + public enum Command {Done, Count}; + /** + * Command + */ + @CaChannel(type=Integer.class, name =".CNT") + private ChannelBean command; + + + public enum Mode {OneShot, AutoCount}; + /** + * Count mode + */ + @CaChannel(type=Integer.class, name =".CONT") + private ChannelBean mode; + + /** + * Channel description + */ + @CaChannel(type=Boolean.class, name={".NM1", ".NM2", ".NM3", ".NM4", ".NM5", ".NM6", ".NM7", ".NM8", ".NM9", ".NM10", ".NM11", ".NM12", ".NM13", ".NM14", ".NM15", ".NM16"}) + private List> channelDescription; + + /** + * Channel gate + */ + @CaChannel(type=Boolean.class, name={".G1", ".G2", ".G3", ".G4", ".G5", ".G6", ".G7", ".G8", ".G9", ".G10", ".G11", ".G12", ".G13", ".G14", ".G15", ".G16"}) + private List> channelGate; + + /** + * Channel preset count + * If gate is on scaler will only count until this value + */ + @CaChannel(type=Integer.class, name={".PR1", ".PR2", ".PR3", ".PR4", ".PR5", ".PR6", ".PR7", ".PR8", ".PR9", ".PR10", ".PR11", ".PR12", ".PR13", ".PR14", ".PR15", ".PR16"}) + private List> channelPresetCount; + + /** + * @return the command + */ + public ChannelBean getCommand() { + return command; + } + + /** + * @return the mode + */ + public ChannelBean getMode() { + return mode; + } + + /** + * @return the channelDescription + */ + public List> getChannelDescription() { + return channelDescription; + } + + /** + * @return the channelGate + */ + public List> getChannelGate() { + return channelGate; + } + + /** + * @return the channelPresetCount + */ + public List> getChannelPresetCount() { + return channelPresetCount; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/manipulator/JythonManipulation.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/manipulator/JythonManipulation.java new file mode 100644 index 0000000..386d08e --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/manipulator/JythonManipulation.java @@ -0,0 +1,261 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.manipulator; + +import gov.aps.jca.CAException; + +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; +import ch.psi.fda.core.scripting.JythonParameterMapping; +import ch.psi.fda.core.scripting.JythonParameterMappingChannel; +import ch.psi.fda.core.scripting.JythonParameterMappingGlobalVariable; +import ch.psi.fda.core.scripting.JythonParameterMappingID; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * Manipulation + * @author ebner + */ +public class JythonManipulation implements Manipulation{ + + + // Get Logger + private static Logger logger = Logger.getLogger(JythonManipulation.class.getName()); + + /** + * Pattern of the entry function of the jython script + */ + private static final String entryFunction = "process"; + private static final String entryFunctionPattern = "def "+entryFunction+"\\((.*)\\):"; + + /** + * Code of the manipulation. The script need to implement a function of the type: + * + * def process(): + * # ... your code + * return value + * + * + * The number of parameters of the script are not limited. So process(a) is valid + * as well as process(a,b,c,d,e,f). + * However for each parameter there need to be a mapping inside the mapping list! If there is no + * mapping inside the Manipulator evaluating this Manipulation will throw an Exception. + */ + private final String script; + + /** + * Id of the resulting component + */ + private final String id; + + /** + * Mapping of components to manipulation code variables + */ + private final List mapping; + + private final boolean returnArray; + + /** + * Script engine of the manipulator + */ + private ScriptEngine engine; + /** + * Component index of the script parameter. The sequence of the indexes in this array correspond to the script + * parameter position, i.e. the first index corresponds to the first parameter. + */ + private Integer[] parameterIndex; + /** + * Parameter array of the entry function + */ + private String[] parameter; + + /** + * Jython entry call + */ + private String jythonCall; + + public JythonManipulation(String id, String script, List mapping){ + this(id, script, mapping, false); + } + + /** + * Constructor + * @param id + * @param script + * @param mapping + */ + public JythonManipulation(String id, String script, List mapping, boolean returnArray){ + this.id = id; + this.script = script; + this.mapping = mapping; + this.returnArray = returnArray; + } + + /** + * @return the script + */ + public String getScript() { + return script; + } + + /** + * @return the mapping + */ + public List getMapping() { + return mapping; + } + + + @Override + public String getId() { + return id; + } + + + @Override + public void initialize(DataMessageMetadata metadata){ + + // Workaround for Jython memory leak + // http://blog.hillbrecht.de/2009/07/11/jython-memory-leakout-of-memory-problem/ + System.setProperty("python.options.internalTablesImpl","weak"); + + // Create new script engine + this.engine = new ScriptEngineManager().getEngineByName("python"); + + // Determine script entry function and the function parameters + Pattern pattern = Pattern.compile(entryFunctionPattern); + Matcher matcher = pattern.matcher(this.script); + if(matcher.find() && matcher.groupCount()==1){ + if(!matcher.group(1).trim().equals("")){ + logger.finest("Entry function '"+entryFunctionPattern+"' found - Identified parameters: "+matcher.group(1)); + parameter = matcher.group(1).split(" *, *"); + } + else{ + parameter = new String[0]; + } + } + else{ + throw new IllegalArgumentException("Cannot determine entry function: "+entryFunctionPattern); + } + + // Load manipulation script + try { + engine.eval(this.script); + } catch (ScriptException e) { + throw new RuntimeException("Unable to load manipulation script", e); + } + + // Determine component index of the needed parameters + // If the component index of the parameter cannot be determined an IllegalArgumentException is thrown + parameterIndex = new Integer[parameter.length]; + for(int i=0;i cb; + try { + cb = ChannelBeanFactory.getFactory().createChannelBean(pm.getType(), pm.getChannel(), true); + } catch (CAException e) { + throw new IllegalArgumentException("Unable to establish channel: "+pm.getChannel(), e); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to establish channel: "+pm.getChannel(), e); + } + + engine.put(pm.getVariable(), cb); + } + else if (jpm instanceof JythonParameterMappingGlobalVariable){ + JythonParameterMappingGlobalVariable pm = (JythonParameterMappingGlobalVariable)jpm; + parameterIndex[i] = null; + + engine.put(pm.getVariable(), pm.getGlobalVariable()); + } + found=true; + break; + } + } + // If there is no mapping nothing can be found + // If there are mappings everything need to be found + if(!found){ + throw new IllegalArgumentException("No mapping compontent found for parameter "+p); + } + } + + StringBuffer buffer = new StringBuffer(); + buffer.append(entryFunction); + buffer.append("("); + + for(String p: parameter){ + buffer.append(p); + buffer.append(","); + } + + buffer.setCharAt(buffer.length()-1, ')'); + + jythonCall = buffer.toString(); + + } + + @Override + public Object execute(DataMessage message){ + + // Manipulate data + for(int i=0;i. + * + */ + +package ch.psi.fda.core.manipulator; + +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; + +/** + * @author ebner + * + */ +public interface Manipulation { + + /** + * Get the id of the manipulation + * @return id of manipulation + */ + public String getId(); + + /** + * Initialize the manipulation + * @param metadata Metadata of the incomming data message + */ + public void initialize(DataMessageMetadata metadata); + + /** + * Execute the manipulation on the passed data message + * @param message Message to manipulate + * @return Result of the manipulation + */ + public Object execute(DataMessage message); +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/manipulator/Manipulator.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/manipulator/Manipulator.java new file mode 100644 index 0000000..e05a101 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/manipulator/Manipulator.java @@ -0,0 +1,123 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.manipulator; + +import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; + +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; + +/** + * @author ebner + * + */ +public class Manipulator implements Runnable{ + + /** + * Outgoing data queue + */ + private final DataQueue outQueue; + + /** + * Incomming data queue + */ + private final DataQueue queue; + + /** + * List of manipulations + */ + private final List manipulations; + + /** + * Constructor + * @param queue + * @param manipulations + */ + // TODO need to support multiple (a list of) manipulation(s) + public Manipulator(DataQueue queue, List manipulations){ + + this.manipulations = manipulations; + + // Create outgoing data metadata + DataMessageMetadata dmetadata = queue.getDataMessageMetadata().clone(); + + // Initialize manipulations and create outgoing metadata + for(Manipulation manipulation: this.manipulations){ + // Initialize manipulation +// manipulation.initialize(queue.getDataMessageMetadata()); + manipulation.initialize(dmetadata); + + // Add manipulation id to metadata + dmetadata.getComponents().add(new ComponentMetadata(manipulation.getId(),0)); // Calculated component always belongs to lowes dimension + } + + this.queue = queue; + this.outQueue = new DataQueue(new LinkedBlockingQueue(1000) , dmetadata ); // Create bounded queue to prevent running out of memory ... + } + + /** + * @return the outQueue + */ + public DataQueue getOutQueue() { + return outQueue; + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + try{ + + // Dispatch Messages + Message message = queue.getQueue().take(); + while(!(message instanceof EndOfStreamMessage)){ + if(message instanceof DataMessage){ + DataMessage dm = (DataMessage) message; + + for(Manipulation manipulation: manipulations){ + dm.getData().add(manipulation.execute(dm)); + } + } + + // Put message to outgoing queue ... + outQueue.getQueue().put(message); + + // Read next message + message = queue.getQueue().take(); + } + + // Write end of stream message + outQueue.getQueue().put(message); + + + } catch (InterruptedException e) { + // TODO Stop loop and exit logic instead of throwing an Exception + throw new RuntimeException("Data manipulator was interrupted while writing data to file",e); + } + } + + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/ComponentMetadata.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/ComponentMetadata.java new file mode 100644 index 0000000..11004d7 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/ComponentMetadata.java @@ -0,0 +1,82 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.messages; + +/** + * Metadata of a component of a message. Each component has a global id. + * Optionally the component can also belong to a dimension. However, depending on the + * view the number of the dimension might vary. Therefore the dimension number + * might change during the lifetime of a message (component). + * + * @author ebner + * + */ +public class ComponentMetadata { + /** + * Global id of the component + */ + private final String id; + + /** + * Dimension of the component (optional) + */ + private final int dimension; + + /** + * Default constructor - Create component metadata + * @param id Global id of the component + */ + public ComponentMetadata(String id){ + this.id = id; + this.dimension = 0; + } + + /** + * Constructor that initializes id and the dimension number + * @param id + * @param dimension + */ + public ComponentMetadata(String id, int dimension){ + this.id = id; + this.dimension = dimension; + } + + /** + * @return the dimension + */ + public int getDimension() { + return dimension; + } + + /** + * @return the id + */ + public String getId() { + return id; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "Component Metadata[id="+id+" dimension="+dimension+"]"; + } +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/ControlMessage.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/ControlMessage.java new file mode 100644 index 0000000..793283c --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/ControlMessage.java @@ -0,0 +1,29 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.messages; + +/** + * A control message that is not holding any data but + * control information (like end of loop, etc.) + * @author ebner + * + */ +public abstract class ControlMessage extends Message { +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/DataMessage.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/DataMessage.java new file mode 100644 index 0000000..f7b4fcb --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/DataMessage.java @@ -0,0 +1,83 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.messages; + +import java.util.ArrayList; +import java.util.List; + +/** + * Message holding data + * @author ebner + * + */ +public class DataMessage extends Message { + + /** + * Data payload of the message + */ + private List data; + + /** + * Constructor - Create message object with data payload + */ + public DataMessage(){ + this.data = new ArrayList(); + } + + /** + * Get data object of the message + * @return Data object of the message + */ + public List getData(){ + return(data); + } + + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + StringBuffer b = new StringBuffer(); + b.append("Message [ "); + for (Object o : data) { + if (o.getClass().isArray()) { + // If the array object is of type double[] display its content + if (o instanceof double[]) { + double[] oa = (double[]) o; + b.append("[ "); + for (double o1 : oa) { + b.append(o1); + b.append(" "); + } + b.append("]"); + } else { + b.append(o.toString()); + } + } else { + b.append(o); + } + + b.append(" "); + } + b.append("]"); + return b.toString(); + } +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/DataMessageMetadata.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/DataMessageMetadata.java new file mode 100644 index 0000000..fd0f67d --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/DataMessageMetadata.java @@ -0,0 +1,81 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.messages; + +import java.util.ArrayList; +import java.util.List; + +/** + * Structure to hold the metadata of a component of a message. + * @author ebner + * + */ +public class DataMessageMetadata { + + /** + * List of the metadata of the message components + */ + private List components; + + /** + * Default constructor + */ + public DataMessageMetadata(){ + components = new ArrayList(); + } + + /** + * @return the list of component metadata of a message described by this object. + */ + public List getComponents() { + return components; + } + + /** + * Get the index of the component with the specified Id + * @param componentId Id of the component to look for + * @return Index of the component + * @throws IllegalArgumentException There is no component with the specified Id + */ + public int getIndex(String componentId) throws IllegalArgumentException { + for(int i=0;i. + * + */ + +package ch.psi.fda.core.messages; + +import java.util.concurrent.BlockingQueue; + + +/** + * Container holding for holding a queue and the metadata of the + * data messages of that queue. + * @author ebner + * + */ +public class DataQueue { + + /** + * Queue serving the messages + */ + private final BlockingQueue queue; + /** + * Metadata of the data messages of the queue + */ + private final DataMessageMetadata dataMessageMetadata; + + /** + * Constructor + * @param queue Data queue + * @param dataMessageMetadata Metadata of the data messages of the queue + */ + public DataQueue(BlockingQueue queue, DataMessageMetadata dataMessageMetadata){ + this.queue = queue; + this.dataMessageMetadata = dataMessageMetadata; + } + + /** + * @return the queue + */ + public BlockingQueue getQueue() { + return queue; + } + + /** + * @return the metadata + */ + public DataMessageMetadata getDataMessageMetadata() { + return dataMessageMetadata; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/EndOfStreamMessage.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/EndOfStreamMessage.java new file mode 100644 index 0000000..d9c9e9b --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/EndOfStreamMessage.java @@ -0,0 +1,59 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.messages; + +/** + * Message that is send at the end of the action loop inside an ActionLoop implementation + * of just to indicate that a particular stream has finished + * @author ebner + * + */ +public class EndOfStreamMessage extends ControlMessage{ + + + /** + * Intersect flag - flag to indicate that stream should be intersected + * after this message. + */ + private final boolean iflag; + + public EndOfStreamMessage(){ + this(false); + } + + public EndOfStreamMessage(boolean iflag){ + this.iflag = iflag; + } + + /** + * @return the iflag + */ + public boolean isIflag(){ + return(iflag); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "Message[ c message: end of stream ]"; + } +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/Message.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/Message.java new file mode 100644 index 0000000..2a36287 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/Message.java @@ -0,0 +1,28 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.messages; + +/** + * Message that can be put to the data queue + * @author ebner + * + */ +public abstract class Message { +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/StreamDelimiterMessage.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/StreamDelimiterMessage.java new file mode 100644 index 0000000..2777e89 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/messages/StreamDelimiterMessage.java @@ -0,0 +1,80 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.messages; + +/** + * Message that is send at the end of the action loop inside an ActionLoop implementation + * @author ebner + * + */ +public class StreamDelimiterMessage extends ControlMessage{ + + /** + * Number of the dimension this delimiter belongs to. + */ + private final int number; + + /** + * Intersect flag - flag to indicate that stream should be intersected + * after this message. + */ + private final boolean iflag; + + /** + * Constructor + * @param number Number of the dimension this delimiter belongs to + */ + public StreamDelimiterMessage(int number){ + this(number, false); + } + + /** + * Constructor + * @param number + * @param iflag Flag to indicate that data is grouped + */ + public StreamDelimiterMessage(int number, boolean iflag){ + this.number = number; + this.iflag = iflag; + } + + /** + * @return the number + */ + public int getNumber() { + return number; + } + + /** + * @return the iflag + */ + public boolean isIflag(){ + return iflag; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + // TODO Auto-generated method stub + return "Message [ c message: delimiter dimension "+number+" ]"; + } +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonGlobalVariable.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonGlobalVariable.java new file mode 100644 index 0000000..c8f6d64 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonGlobalVariable.java @@ -0,0 +1,55 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.scripting; + +/** + * @author ebner + * + */ +public class JythonGlobalVariable { + private String name; + private double value; + + /** + * @return the name + */ + public String getName() { + return name; + } + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + /** + * @return the value + */ + public double getValue() { + return value; + } + /** + * @param value the value to set + */ + public void setValue(double value) { + this.value = value; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonGlobalVariableDictionary.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonGlobalVariableDictionary.java new file mode 100644 index 0000000..10a4ccb --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonGlobalVariableDictionary.java @@ -0,0 +1,75 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.scripting; + +import java.util.HashMap; + +/** + * Singleton dictionary class + * + * @author ebner + * + */ +public class JythonGlobalVariableDictionary { + + private static JythonGlobalVariableDictionary instance = new JythonGlobalVariableDictionary(); + private HashMap dictionary = new HashMap(); + + /** + * Private constructor + */ + private JythonGlobalVariableDictionary(){ + } + + /** + * Get singleton instance of the dictionary + * @return + */ + public static JythonGlobalVariableDictionary getInstance(){ + return instance; + } + + + /** + * Get variable from dictionary. If the variable does not exist it will be created. + * @param name + * @return + */ + public JythonGlobalVariable getVariable(String name){ + JythonGlobalVariable variable; + if(dictionary.containsKey(name)){ + variable = dictionary.get(name); + } + else{ + variable = new JythonGlobalVariable(); + variable.setName(name); + dictionary.put(name, variable); + } + + return variable; + } + + /** + * Clear variable dictionary + */ + public void clear(){ + dictionary.clear(); + } +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonParameterMapping.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonParameterMapping.java new file mode 100644 index 0000000..d96d175 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonParameterMapping.java @@ -0,0 +1,55 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.scripting; + +/** + * Mapping of a parameter to something + * @author ebner + * + */ +public abstract class JythonParameterMapping { + + /** + * Variable name inside the script + */ + private String variable; + + /** + * Constructor + * @param variable + */ + public JythonParameterMapping(String variable){ + this.variable = variable; + } + + /** + * @return the variable + */ + public String getVariable() { + return variable; + } + /** + * @param variable the variable to set + */ + public void setVariable(String variable) { + this.variable = variable; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonParameterMappingChannel.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonParameterMappingChannel.java new file mode 100644 index 0000000..d5ac750 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonParameterMappingChannel.java @@ -0,0 +1,75 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.scripting; + + +/** + * Mapping of a script parameter to a channel bean. + * @author ebner + * + */ +public class JythonParameterMappingChannel extends JythonParameterMapping { + + /** + * Id of the component to map to this variable + */ + private String channel; + private Class type; + + /** + * Constructor accepting varible/id pair + * @param variable + * @param channel + * @param type + */ + public JythonParameterMappingChannel(String variable, String channel, Class type){ + super(variable); + this.channel = channel; + this.type = type; + } + + /** + * @return the channel + */ + public String getChannel() { + return channel; + } + /** + * @param channel + */ + public void setChannel(String channel) { + this.channel = channel; + } + + /** + * @return the type + */ + public Class getType() { + return type; + } + + /** + * @param type the type to set + */ + public void setType(Class type) { + this.type = type; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonParameterMappingGlobalVariable.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonParameterMappingGlobalVariable.java new file mode 100644 index 0000000..509cde4 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonParameterMappingGlobalVariable.java @@ -0,0 +1,56 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.scripting; + + +/** + * Mapping of a script parameter to a component via the Id. + * @author ebner + * + */ +public class JythonParameterMappingGlobalVariable extends JythonParameterMapping { + + private JythonGlobalVariable globalVariable; + + /** + * Constuctor + * @param variable + * @param globalVariable + */ + public JythonParameterMappingGlobalVariable(String variable, JythonGlobalVariable globalVariable){ + super(variable); + this.globalVariable = globalVariable; + } + + /** + * @return the globalVariable + */ + public JythonGlobalVariable getGlobalVariable() { + return globalVariable; + } + + /** + * @param globalVariable the globalVariable to set + */ + public void setGlobalVariable(JythonGlobalVariable globalVariable) { + this.globalVariable = globalVariable; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonParameterMappingID.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonParameterMappingID.java new file mode 100644 index 0000000..e899c4b --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/scripting/JythonParameterMappingID.java @@ -0,0 +1,58 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.scripting; + + +/** + * Mapping of a script parameter to a component via the Id. + * @author ebner + * + */ +public class JythonParameterMappingID extends JythonParameterMapping { + + /** + * Id of the component to map to this variable + */ + private String refid; + + /** + * Constructor accepting varible/id pair + * @param variable + * @param refid + */ + public JythonParameterMappingID(String variable, String refid){ + super(variable); + this.refid = refid; + } + + /** + * @return the refid + */ + public String getRefid() { + return refid; + } + /** + * @param refid the refid to set + */ + public void setRefid(String refid) { + this.refid = refid; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/ChannelAccessDoubleArraySensor.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/ChannelAccessDoubleArraySensor.java new file mode 100644 index 0000000..0b81103 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/ChannelAccessDoubleArraySensor.java @@ -0,0 +1,144 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.sensors; + +import java.util.logging.Logger; + +import gov.aps.jca.CAException; +import ch.psi.fda.core.EngineConfiguration; +import ch.psi.fda.core.Sensor; +import ch.psi.jcae.ChannelBeanFactory; +import ch.psi.jcae.ChannelBean; + +/** + * Scalar sensor that reads a double form a Channel Access channel + * @author ebner + * + */ +public class ChannelAccessDoubleArraySensor implements Sensor { + + // Get Logger + private static Logger logger = Logger.getLogger(ChannelAccessDoubleArraySensor.class.getName()); + + /** + * Channel Access channel of this sensor + */ + private final ChannelBean channel; + + /** + * Number of elements to read from the waveform + */ + private final int numberOfElements; + + /** + * Global id of the sensor + */ + private final String id; + + /** + * Constructor + * @param id Global id of the sensor + * @param channelName Name of the Channel Access channel of this sensor + * @param numberOfElements Number of elements to read out of the waveform + */ + public ChannelAccessDoubleArraySensor(String id, String channelName, int numberOfElements){ + try { + this.channel = ChannelBeanFactory.getFactory().createChannelBean(double[].class, channelName, false); + this.numberOfElements = numberOfElements; + this.id = id; + } catch (CAException e) { + // Convert Exception into unchecked RuntimeException + throw new IllegalArgumentException("Unable to initialize sensor channel [name:"+channelName+"]",e); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to initialize sensor channel [name:"+channelName+"]",e); + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#readout() + */ + @Override + public Object read() throws InterruptedException { + logger.finest("Read sensor "+channel.getName()); + + double[] v; + try { + v = channel.getValue(numberOfElements); + } catch (IllegalStateException e) { + // Only fail during data acquisition if fail on error sensor flag is on true. Otherwise + // return NaN (Not a Number) + if(EngineConfiguration.getInstance().isFailOnSensorError()){ + throw e; + } + v = new double[numberOfElements]; + for(int i =0;i. + * + */ + +package ch.psi.fda.core.sensors; + +import java.util.logging.Logger; + +import gov.aps.jca.CAException; +import ch.psi.fda.core.EngineConfiguration; +import ch.psi.fda.core.Sensor; +import ch.psi.jcae.ChannelBeanFactory; +import ch.psi.jcae.ChannelBean; + +/** + * Scalar sensor that reads a double form a Channel Access channel + * @author ebner + * + */ +public class ChannelAccessDoubleSensor implements Sensor { + + + // Get Logger + private static Logger logger = Logger.getLogger(ChannelAccessDoubleSensor.class.getName()); + + /** + * Channel Access channel of this sensor + */ + private ChannelBean channel; + + /** + * Global id of the sensor + */ + private final String id; + + /** + * Constructor + * @param id Global id of the sensor + * @param channelName Name of the Channel Access channel of this sensor + */ + public ChannelAccessDoubleSensor(String id, String channelName){ + try { + channel = ChannelBeanFactory.getFactory().createChannelBean(Double.class, channelName, false); + this.id = id; + } catch (CAException e) { + // Convert Exception into unchecked RuntimeException + throw new IllegalArgumentException("Unable to initialize sensor channel [name:"+channelName+"]",e); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to initialize sensor channel [name:"+channelName+"]",e); + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#readout() + */ + @Override + public Object read() throws InterruptedException { + logger.finest("Read sensor "+channel.getName()); + + Double v; + try { + v = channel.getValue(); + } catch (IllegalStateException e) { + // Only fail during data acquisition if fail on error sensor flag is on true. Otherwise + // return NaN (Not a Number) + if(EngineConfiguration.getInstance().isFailOnSensorError()){ + throw e; + } + v = Double.NaN; + } catch (CAException e) { + // Only fail during data acquisition if fail on error sensor flag is on true. Otherwise + // return NaN (Not a Number) + if(EngineConfiguration.getInstance().isFailOnSensorError()){ + // Convert Exception into unchecked RuntimeException + throw new RuntimeException("Unable to get value from channel [name:"+channel.getName()+"]",e); + } + v = Double.NaN; +// } catch (InterruptedException e) { +// if(EngineConfiguration.getInstance().isFailOnSensorError()){ +// throw new RuntimeException("Unable to get value from channel [name:"+channel.getName()+"]",e); +// } +// v = Double.NaN; + } + return(v); + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#getId() + */ + @Override + public String getId() { + return id; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#destroy() + */ + @Override + public void destroy() { + // Destroy channel + try { + logger.finest("Destroy sensor channel: "+channel.getName()); + channel.destroy(); + } catch (CAException e) { + throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e); + } + } + + /** + * Get channel object of sensor + * @return + */ + public ChannelBean getChannel(){ + return channel; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/ChannelAccessStringSensor.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/ChannelAccessStringSensor.java new file mode 100644 index 0000000..7dcad23 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/ChannelAccessStringSensor.java @@ -0,0 +1,124 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.sensors; + +import java.util.logging.Logger; + +import gov.aps.jca.CAException; +import ch.psi.fda.core.EngineConfiguration; +import ch.psi.fda.core.Sensor; +import ch.psi.jcae.ChannelBeanFactory; +import ch.psi.jcae.ChannelBean; + +/** + * Scalar sensor that reads a double form a Channel Access channel + * @author ebner + * + */ +public class ChannelAccessStringSensor implements Sensor { + + + // Get Logger + private static Logger logger = Logger.getLogger(ChannelAccessStringSensor.class.getName()); + + /** + * Channel Access channel of this sensor + */ + private ChannelBean channel; + + /** + * Global id of the sensor + */ + private final String id; + + /** + * Constructor + * @param id Global id of the sensor + * @param channelName Name of the Channel Access channel of this sensor + */ + public ChannelAccessStringSensor(String id, String channelName){ + try { + channel = ChannelBeanFactory.getFactory().createChannelBean(String.class, channelName, false); + this.id = id; + } catch (CAException e) { + // Convert Exception into unchecked RuntimeException + throw new IllegalArgumentException("Unable to initialize sensor channel [name:"+channelName+"]",e); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to initialize sensor channel [name:"+channelName+"]",e); + } + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#readout() + */ + @Override + public Object read() throws InterruptedException { + logger.finest("Read sensor "+channel.getName()); + + String v; + try { + v = channel.getValue(); + } catch (IllegalStateException e) { + // Only fail during data acquisition if fail on error sensor flag is on true. Otherwise + // return NaN (Not a Number) + if(EngineConfiguration.getInstance().isFailOnSensorError()){ + throw e; + } + v = null; + } catch (CAException e) { + // Only fail during data acquisition if fail on error sensor flag is on true. Otherwise + // return NaN (Not a Number) + if(EngineConfiguration.getInstance().isFailOnSensorError()){ + // Convert Exception into unchecked RuntimeException + throw new RuntimeException("Unable to get value from channel [name:"+channel.getName()+"]",e); + } + v = null; +// } catch (InterruptedException e) { +// if(EngineConfiguration.getInstance().isFailOnSensorError()){ +// throw new RuntimeException("Unable to get value from channel [name:"+channel.getName()+"]",e); +// } +// v = null; + } + return(v); + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#getId() + */ + @Override + public String getId() { + return id; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#destroy() + */ + @Override + public void destroy() { + // Destroy channel + try { + logger.finest("Destroy sensor channel: "+channel.getName()); + channel.destroy(); + } catch (CAException e) { + throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e); + } + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/ComplexSensor.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/ComplexSensor.java new file mode 100644 index 0000000..59b04f4 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/ComplexSensor.java @@ -0,0 +1,136 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.sensors; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +import ch.psi.fda.core.Action; +import ch.psi.fda.core.Sensor; + +/** + * Complex sensor that complements an other sensor with pre and post actions. + * Before reading out the complemented sensor the pre actions are executed. After the + * readout the post actions. + * + * @author ebner + * + */ +public class ComplexSensor implements Sensor { + + // Get Logger + private static Logger logger = Logger.getLogger(ComplexSensor.class.getName()); + + /** + * Id of the sensor + */ + private String id; + + /** + * Sensor to complement + */ + private final Sensor sensor; + + /** + * Actions that are executed directly before the first step of this actor + */ + private final List preActions; + + /** + * Actions that are executed directly after the last step of this actor + */ + private final List postActions; + + /** + * Constructor + */ + public ComplexSensor(String id, Sensor sensor){ + this.id = id; + + this.sensor = sensor; + this.preActions = new ArrayList(); + this.postActions = new ArrayList(); + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#read() + */ + @Override + public Object read() throws InterruptedException { + // Execute pre actions + logger.finest("Execute pre actions"); + for(Action action: preActions){ + action.execute(); + } + + // Readout sensor + Object value = sensor.read(); + + // Execute post actions + logger.finest("Execute post actions"); + for(Action action: postActions){ + action.execute(); + } + + return value; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#getId() + */ + @Override + public String getId() { + return id; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#destroy() + */ + @Override + public void destroy() { + // Destroy preActions + for(Action a: preActions){ + a.destroy(); + } + + sensor.destroy(); + + // Destroy postActions + for(Action a: postActions){ + a.destroy(); + } + } + + /** + * @return the preActions + */ + public List getPreActions() { + return preActions; + } + + /** + * @return the postActions + */ + public List getPostActions() { + return postActions; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/MillisecondTimestampSensor.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/MillisecondTimestampSensor.java new file mode 100644 index 0000000..5c53e35 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/MillisecondTimestampSensor.java @@ -0,0 +1,69 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.sensors; + +import ch.psi.fda.core.Sensor; + +/** + * Get the current time in milliseconds + * @author ebner + * + */ +public class MillisecondTimestampSensor implements Sensor { + + /** + * Global id of the sensor + */ + private final String id; + + /** + * Constructor + * @param id Global id of the sensor + */ + public MillisecondTimestampSensor(String id){ + this.id = id; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#read() + */ + @Override + public Object read() { + // Return current time in milliseconds + return new Double(System.currentTimeMillis()); + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#getId() + */ + @Override + public String getId() { + return id; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#destroy() + */ + @Override + public void destroy() { + // Nothing to be done + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/OTFNamedChannelSensor.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/OTFNamedChannelSensor.java new file mode 100644 index 0000000..315019f --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/OTFNamedChannelSensor.java @@ -0,0 +1,84 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.sensors; + +import ch.psi.fda.core.Sensor; + +/** + * Sensor to read out a named (Epics) channel. This sensor can only be used within the + * OTFLoop. If it is used in other loops, the read value will always be 0. + * @author ebner + * + */ +public class OTFNamedChannelSensor implements Sensor { + + /** + * Name of the channel + */ + private final String name; + + /** + * Global id of the sensor + */ + private final String id; + + /** + * Constructor + * @param id Global id of the sensor + * @param name Name of the (Epics) channel + */ + public OTFNamedChannelSensor(String id, String name){ + this.id = id; + this.name = name; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#read() + */ + @Override + public Object read() { + // Always return 0 if read() method is called. + return 0d; + } + + /** + * @return the name of the channel + */ + public String getName() { + return name; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#getId() + */ + @Override + public String getId() { + return id; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#destroy() + */ + @Override + public void destroy() { + // Nothing to be done + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/OTFReadbackSensor.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/OTFReadbackSensor.java new file mode 100644 index 0000000..83b370f --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/OTFReadbackSensor.java @@ -0,0 +1,70 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.sensors; + +import ch.psi.fda.core.Sensor; + +/** + * Sensor to read actuator readback. This sensor must only be used within the + * OTFLoop. If it is used in other loops, the read value will always be 0. + * @author ebner + * + */ +public class OTFReadbackSensor implements Sensor { + + /** + * Global id of the sensor + */ + private final String id; + + /** + * Constructor + * @param id Global id of the sensor + */ + public OTFReadbackSensor(String id){ + this.id = id; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#read() + */ + @Override + public Object read() { + // Always return 0 if read() method is called. + return 0d; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#getId() + */ + @Override + public String getId() { + return id; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#destroy() + */ + @Override + public void destroy() { + // Nothing to be done + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/OTFScalerChannelSensor.java b/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/OTFScalerChannelSensor.java new file mode 100644 index 0000000..152485b --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/core/sensors/OTFScalerChannelSensor.java @@ -0,0 +1,84 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.sensors; + +import ch.psi.fda.core.Sensor; + +/** + * Sensor to read out a scaler channel. This sensor can only be used within the + * OTFLoop. If it is used in other loops, the read value will always be 0. + * @author ebner + * + */ +public class OTFScalerChannelSensor implements Sensor { + + /** + * Index of the scaler channel. The index starts at 0; + */ + private final int index; + + /** + * Global id of the sensor + */ + private final String id; + + /** + * Constructor + * @param id Global id of the sensor + * @param index Index of the scaler channel. Index starts at 0. + */ + public OTFScalerChannelSensor(String id, int index){ + this.id = id; + this.index = index; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#read() + */ + @Override + public Object read() { + // Always return 0 if read() method is called. + return 0d; + } + + /** + * @return the index of the scaler channel + */ + public int getIndex() { + return index; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#getId() + */ + @Override + public String getId() { + return id; + } + + /* (non-Javadoc) + * @see ch.psi.fda.core.Sensor#destroy() + */ + @Override + public void destroy() { + // Nothing to be done + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/deserializer/DataDeserializer.java b/ch.psi.fda/src/main/java/ch/psi/fda/deserializer/DataDeserializer.java new file mode 100644 index 0000000..a7e56c5 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/deserializer/DataDeserializer.java @@ -0,0 +1,36 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.deserializer; + +import ch.psi.fda.core.messages.DataQueue; + +/** + * Data deserializer + * @author ebner + * + */ +public interface DataDeserializer extends Runnable { + + /** + * Get data queue of deserializer + * @return data queue of deserializer + */ + public DataQueue getQueue(); +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/deserializer/DataDeserializerMDA.java b/ch.psi.fda/src/main/java/ch/psi/fda/deserializer/DataDeserializerMDA.java new file mode 100644 index 0000000..0e9deec --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/deserializer/DataDeserializerMDA.java @@ -0,0 +1,603 @@ +package ch.psi.fda.deserializer; + +import hep.io.xdr.XDRInputStream; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.Logger; + +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; +import ch.psi.fda.core.messages.StreamDelimiterMessage; + +public class DataDeserializerMDA implements DataDeserializer { + + private static Logger logger = Logger.getLogger(DataDeserializerMDA.class.getName()); + + private DataQueue queue; + + public DataDeserializerMDA(File file){ + + try { + RecursiveReturnContainer c = read(new FileInputStream(file)); + + this.queue = new DataQueue(new LinkedBlockingQueue(), c.getMetadata()); + + // Add data to queue + for(Message m: c.getMessage()){ + queue.getQueue().put(m); + } + queue.getQueue().put(new EndOfStreamMessage()); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + public RecursiveReturnContainer read(InputStream in) throws IOException { + logger.fine("Read MDA input stream"); + + XDRInputStream x = new XDRInputStream(in); + + + /** + * Read file header + */ + float version = x.readFloat(); + logger.fine("MDA - version:: "+version); + int scanNumber = x.readInt(); + logger.fine("MDA - scan number:: "+scanNumber); + int rank = x.readInt(); + logger.fine("MDA - rank:: "+rank); + + for(int i=rank;i>0; i--){ + // Read dimension size + int dimension = x.readInt(); + logger.fine("MDA - dimension["+i+"] :: "+dimension); + } + + int isRegular = x.readInt(); //(true=1, false=0) + logger.fine("MDA - isRegular:: "+isRegular); + int pExtra = x.readInt(); // Number of extra pvs + logger.fine("MDA - pExtra:: "+pExtra); + + + /** + * Read data + */ + RecursiveReturnContainer container = readData(x); + + /** + * Read extra PVs + */ + if(pExtra > 0){ + int length; + logger.fine("Extra PVs"); + + int numExtra = x.readInt(); + logger.fine("MDA - number of extra pvs:: "+numExtra); + + for(int i = 0;i 0){ + String pvName = x.readString(); + logger.fine("MDA - pv name:: "+pvName); + } + + length = x.readInt(); + if(length > 0){ + String pvDescription = x.readString(); // description + logger.fine("MDA - pv desciption:: "+pvDescription); + } + + int pvType = x.readInt(); + logger.fine("MDA - pv type:: "+pvType); + + + int pvCount = 0; + String pvUnit; + if(pvType != 0){ // if pv type != DBR_STRING + pvCount = x.readInt(); + logger.fine("MDA - pv count:: "+pvCount); + length = x.readInt(); + if(length > 0){ + pvUnit = x.readString(); + logger.fine("MDA - pv unit:: "+ pvUnit); + } + } + + + if(pvType == 0){ // pvType == DBR_STRING + length = x.readInt(); + if(length > 0){ + String pvValue = x.readString(); + logger.fine("MDA - pv value:: "+pvValue); + } + } + else if(pvType == 32){ // pvType == DBR_CTRL_CHAR + for(int u=0;u 1){ + // For 1D scans this block is never reached + for(int i=0;i 0){ + String scanName = x.readString(); + logger.fine("MDA - scanName:: "+scanName); + } + + length = x.readInt(); + if(length > 0){ + String scanTime = x.readString(); // timestamp + logger.fine("MDA - scanTime:: "+ scanTime); + } + + + int numberOfPositioners = x.readInt(); + logger.fine("MDA - number of positioners:: "+numberOfPositioners); + int numberOfDetectors = x.readInt(); + logger.fine("MDA - number of detectors:: "+ numberOfDetectors); + int numberOfTriggers = x.readInt(); + logger.fine("MDA - number of triggers:: "+ numberOfTriggers); + + + List componentMetadataList = new ArrayList(); + /** + * Read positioners metadata + */ + for(int i = 0; i0){ + String positionerName = x.readString(); + logger.fine("MDA - positioner name:: "+positionerName); + // MDA starts at dimension number 1 we start at 0 + componentMetadataList.add(new ComponentMetadata(positionerName,scanRank-1)); + } + + length = x.readInt(); + if(length > 0){ + String positionerDescription = x.readString(); + logger.fine("MDA - positioner description:: "+positionerDescription); + } + + length = x.readInt(); + if(length > 0){ + String positionerStepMode = x.readString(); + logger.fine("MDA - positioner step mode:: "+positionerStepMode); + } + + length = x.readInt(); + if(length > 0){ + String positionerUnit = x.readString(); + logger.fine("MDA - positioner unit:: "+positionerUnit); + } + + length = x.readInt(); + if(length > 0){ + String readbackName = x.readString(); + logger.fine("MDA - readback name:: "+readbackName); + } + + length = x.readInt(); + if(length > 0){ + String readbackDescription = x.readString(); + logger.fine("MDA - readback description:: "+readbackDescription); + } + + length = x.readInt(); + if(length > 0){ + String readbackUnit = x.readString(); + logger.fine("MDA - readback unit:: "+readbackUnit); + } + } + + /** + * Read detector metadata + */ + for(int i=0; i 0){ + String detectorName = x.readString(); + logger.fine("MDA - detector name:: "+detectorName); + // MDA starts at dimension number 1 we start at 0 + componentMetadataList.add(new ComponentMetadata(detectorName, scanRank-1)); + } + + length = x.readInt(); + if(length > 0){ + String detectorDescription = x.readString(); + logger.fine("MDA - detector description:: "+detectorDescription); + } + + length = x.readInt(); + if(length > 0){ + String detectorUnit = x.readString(); + logger.fine("MDA - detector unit:: "+detectorUnit); + } + } + + /** + * Read trigger metadata + */ + for(int i=0; i 0){ + String triggerName = x.readString(); + logger.fine("MDA - trigger name:: "+triggerName); + } + + float triggerCommand = x.readFloat(); + logger.fine("MDA - trigger command:: "+triggerCommand); + } + + + ArrayList data = new ArrayList(); + + /** + * Read positioner data (readback) + */ + for(int i = 0; i 1){ + /** + * Conversion logic if scan rank is > 1 + */ + + /** + * Read all scans recursively + */ + for(int i=0;i message = new ArrayList(); + private DataMessageMetadata metadata = new DataMessageMetadata(); + + public List getMessage() { + return message; + } + public DataMessageMetadata getMetadata() { + return metadata; + } +} + +/** +* +* http://www.aps.anl.gov/bcda/synApps/sscan/saveData_fileFormat.txt +* +* scan file format + +* FILE HEADER +* xdr_float: VERSION (1.3) +* xdr_long: scan number +* xdr_short data's rank +* xdr_vector(rank, xdr_int) dims; +* xdr_int isRegular (true=1, false=0) +* xdr_long: pointer to the extra pvs +* +* +* SCAN +* HEADER: +* xdr_short: this scan's rank +* xdr_long: number of requested points (NPTS) +* xdr_long: current point (CPT) +* if the scan rank is > 1 +* xdr_vector(NPTS, xdr_long) pointer to the lower scans +* +* INFO: +* xdr_counted_string: scan name +* xdr_counted_string: time stamp +* +* +* xdr_int: number of positioners +* xdr_int: number of detectors +* xdr_int: number of triggers +* +* for each positioner +* xdr_int: positioner number +* xdr_counted_string: positioner name +* xdr_counted_string: positioner desc +* xdr_counted_string: positioner step mode +* xdr_counted_string: positioner unit +* xdr_counted_string: readback name +* xdr_counted_string: readback description +* xdr_counted_string: readback unit +* +* for each detector +* xdr_int: detector number +* xdr_counted_string: detector name +* xdr_counted_string: detector desc +* xdr_counted_string: detector unit +* +* for each trigger +* xdr_int: trigger number +* xdr_counted_string: trigger name +* xdr_float: trigger command +* +* DATA: +* for each positioner +* xdr_vector(NPTS, xdr_double): readback array +* +* for each detector +* xdr_vector(NPTS, xdr_float): detector array +* +* [SCAN] +* ... +* ... +* ... +* [SCAN] +* +* EXTRA PVs +* xdr_int: number of extra pvs +* +* for each pv +* xdr_counted_string: name +* xdr_counted_string: desc +* xdr_int: type +* if type != DBR_STRING +* xdr_long: count +* xdr_counted_string: unit +* +* depending on the type: +* DBR_STRING: +* xdr_counted_string: value +* DBR_CTRL_CHAR: +* xdr_vector(count, xdr_char): value +* DBR_CTRL_SHORT: +* xdr_vector(count, xdr_short): value +* DBR_CTRL_LONG: +* xdr_vector(count, xdr_long): value +* DBR_CTRL_FLOAT: +* xdr_vector(count, xdr_float): value +* DBR_CTRL_DOUBLE: +* xdr_vector(count, xdr_double): value +* ----------------------------------------------------------------------- +* +* A 1D scan looks like this: +* +* header +* extra PV's +* +* A 2D scan looks like this +* +* header +* scan2 +* scan1 +* scan1 +* ... +* extra PV's +* +* A 3D scan looks like this +* +* header +* scan3 +* scan2 +* scan1 +* scan1 +* ... +* scan2 +* scan1 +* scan1 +* ... +* ... +* extra PV's +* +*/ \ No newline at end of file diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/deserializer/DataDeserializerTXT.java b/ch.psi.fda/src/main/java/ch/psi/fda/deserializer/DataDeserializerTXT.java new file mode 100644 index 0000000..04195ba --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/deserializer/DataDeserializerTXT.java @@ -0,0 +1,233 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.deserializer; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.Logger; + +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.StreamDelimiterMessage; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; + +/** + * Deserialize file data and put it into the DataQueue + * @author ebner + * + */ +public class DataDeserializerTXT implements DataDeserializer { + + // Get Logger + private static Logger logger = Logger.getLogger(DataDeserializerTXT.class.getName()); + + private DataQueue queue; + private File file; + + private List dindex; + private List iindex; + + /** + * Default Constructor + * @param file + */ + public DataDeserializerTXT(File file){ + this.file = file; + this.dindex = new ArrayList(); + this.iindex = new ArrayList(); + + DataMessageMetadata metadata; + try{ + // Read metadata + // Open file + BufferedReader reader = new BufferedReader(new FileReader(file)); + + // Read file + String line; + + // First line is id + line = reader.readLine(); + line = line.replaceAll("^ *# *", ""); + String[] ids = line.split("\t"); + + // Second line dimension + line = reader.readLine(); + line = line.replaceAll("^ *# *", ""); + String[] dimensions = line.split("\t"); + + // Create data message metadata + metadata = new DataMessageMetadata(); + Integer d = -1; + for(int i=0;i(10000000), metadata); + } + + /* (non-Javadoc) + * @see ch.psi.fda.deserializer.DataDeserializer#getQueue() + */ + @Override + public DataQueue getQueue(){ + return(queue); + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + try{ + + List checklist = new ArrayList(dindex.size()); + for(int i=0;i0){ + Double d = (Double) message.getData().get(i); +// if(checklist.get(i)==null){ +// checklist.set(i, d); +// } + if(checklist.get(i)!=null &&!checklist.get(i).equals(d)){ + // If value changes issue a dimension delimiter message + queue.getQueue().put(new StreamDelimiterMessage(dindex.get(t)-1)); + } + checklist.set(i, d); + } + } + + // Put message to queue + queue.getQueue().put(message); + + // TODO Need to detect dimension boundaries + + } + + // Add delimiter for all the dimensions + for(int i=dindex.size()-1;i>=0;i--){ + queue.getQueue().put(new StreamDelimiterMessage(dindex.get(i))); + } +// queue.getQueue().put(new DimensionDelimiterMessage(dindex.get(0)-1)); +// queue.getQueue().put(new DimensionDelimiterMessage(dindex.get(0))); + + // Place end of stream message + queue.getQueue().put(new EndOfStreamMessage()); + + // Close file + reader.close(); + + + } catch (InterruptedException e) { + throw new RuntimeException("Data deserializer was interrupted while reading the datafile",e); + } catch (IOException e) { + throw new RuntimeException("Data deserializer had a problem reading the specified datafile",e); + } + + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/gui/ProgressPanel.java b/ch.psi.fda/src/main/java/ch/psi/fda/gui/ProgressPanel.java new file mode 100644 index 0000000..342f4a9 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/gui/ProgressPanel.java @@ -0,0 +1,78 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +/* + * ScanProgressPanel.java + * + * Created on Dec 1, 2009, 2:20:26 PM + */ + +package ch.psi.fda.gui; + +import java.awt.event.ActionListener; + +public class ProgressPanel extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + + /** Creates new form ScanProgressPanel */ + public ProgressPanel() { + initComponents(); + } + + /** This method is called from within the constructor to + * initialize the form. + */ + private void initComponents() { + + progressBar = new javax.swing.JProgressBar(); + abortButton = new javax.swing.JButton(); + + progressBar.setIndeterminate(true); + progressBar.setPreferredSize(new java.awt.Dimension(100, 24)); + + abortButton.setText("Abort"); + abortButton.setFocusable(false); + abortButton.setPreferredSize(new java.awt.Dimension(70, 25)); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(progressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 152, Short.MAX_VALUE) + .addComponent(abortButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(abortButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + } + + + + public void addActionListener(ActionListener l){ + abortButton.addActionListener(l); + } + + public void done(){ + progressBar.setIndeterminate(false); + progressBar.setValue(100); + abortButton.setEnabled(false); + } + + // Variables declaration - do not modify + private javax.swing.JButton abortButton; + private javax.swing.JProgressBar progressBar; + // End of variables declaration +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/gui/ScrollableFlowPanel.java b/ch.psi.fda/src/main/java/ch/psi/fda/gui/ScrollableFlowPanel.java new file mode 100644 index 0000000..cf69fc6 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/gui/ScrollableFlowPanel.java @@ -0,0 +1,57 @@ +package ch.psi.fda.gui; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Rectangle; + +import javax.swing.JPanel; +import javax.swing.Scrollable; +import javax.swing.SwingConstants; + +public class ScrollableFlowPanel extends JPanel implements Scrollable { + + private static final long serialVersionUID = 1L; + + public void setBounds(int x, int y, int width, int height) { + super.setBounds(x, y, getParent().getWidth(), height); + } + + public Dimension getPreferredSize() { + return new Dimension(getWidth(), getPreferredHeight()); + } + + public Dimension getPreferredScrollableViewportSize() { + return super.getPreferredSize(); + } + + public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { + int hundredth = (orientation == SwingConstants.VERTICAL ? getParent().getHeight() : getParent().getWidth()) / 100; + return (hundredth == 0 ? 1 : hundredth); + } + + public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { + return orientation == SwingConstants.VERTICAL ? getParent().getHeight() : getParent().getWidth(); + } + + public boolean getScrollableTracksViewportWidth() { + return true; + } + + public boolean getScrollableTracksViewportHeight() { + return false; + } + + private int getPreferredHeight() { + int rv = 0; + for (int k = 0, count = getComponentCount(); k < count; k++) { + Component comp = getComponent(k); + Rectangle r = comp.getBounds(); + int height = r.y + r.height; + if (height > rv) + rv = height; + } + rv += ((FlowLayout) getLayout()).getVgap(); + return rv; + } +} \ No newline at end of file diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/install/ApplicationConfigurator.java b/ch.psi.fda/src/main/java/ch/psi/fda/install/ApplicationConfigurator.java new file mode 100644 index 0000000..f6fb555 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/install/ApplicationConfigurator.java @@ -0,0 +1,337 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.install; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.InetAddress; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.URISyntaxException; +import java.util.Enumeration; +import java.util.List; +import java.util.logging.Logger; + +/** + * @author ebner + * + */ +public class ApplicationConfigurator { + + // Get Logger + private static final Logger logger = Logger.getLogger(ApplicationConfigurator.class.getName()); + + public final static String FDA_HOME_ARGUMENT = "ch.psi.fda.home"; + public final static String FDA_CONFIG_FILE_ARGUMENT = "ch.psi.fda.config.file"; + + private final File home; + private final File configdir; + private final File datadir; + private final File logdir; + private final File scandir; + + private final File jcaeProperties; + private final File fdaProperties; + private final File loggingProperties; + + /** + * Constructor: The constructor will determine the home directory based on the + * System property fda.home passed via the VM option -Dfda.home=dir or, if not present, + * via the source code (jar) location (this will fail if the source code is not loaded from a file source). + */ + public ApplicationConfigurator(){ + this.home = determineHome(); + this.configdir = new File(home, "config"); + this.datadir = new File(home, "data"); + this.logdir = new File(home, "logs"); + this.scandir = new File(home, "scans"); + + this.jcaeProperties = new File(configdir, "jcae.properties"); + this.fdaProperties = new File(configdir, "fda.properties"); + this.loggingProperties = new File(configdir, "logging.properties"); + } + + + public void initializeApplication(){ + + // Overwrite system property + System.setProperty(FDA_HOME_ARGUMENT, this.home.getAbsolutePath()); + + createApplicationHome(); + createDefaultConfigurationFiles(); + + // Configure used packages / e.g. via setting system/environment parameters, etc. + // Set logging configuration -Djava.util.logging=... + String property = System.getProperty("java.util.logging.config.file"); + if(property==null){ + System.setProperty("java.util.logging.config.file", loggingProperties.getAbsolutePath()); + } + + // Set FDA configuration argument -Dch.psi.fda.config.file=... + property = System.getProperty(FDA_CONFIG_FILE_ARGUMENT); + if(property==null){ + System.setProperty(FDA_CONFIG_FILE_ARGUMENT, fdaProperties.getAbsolutePath()); + } + + // Set jcae.properties file + property = System.getProperty("ch.psi.jcae.config.file"); + if(property==null){ + System.setProperty("ch.psi.jcae.config.file", jcaeProperties.getAbsolutePath()); + } + } + + /** + * Function to determine home directory. This function assumes that the application directories are + * ordered as follows: + * HOME + * |- + * | |-lib + * | | |- + * | |-bin + * |-config + * |-logs + * |-data + * |-scans + * | |-templates + * | |-users + * @return + */ + public File determineHome(){ + File home = null; + + // Check whether fda.home System property is set + String s = System.getProperty(FDA_HOME_ARGUMENT); + if(s!=null){ + // A system property is specified + home = new File(s); + } + else{ + // No system property is specified + + // Try to determine home location based on the location of the sourcecode + try { + // Note: Obviously, this will do odd things if you class was loaded from a non-file location. + // http://stackoverflow.com/questions/320542/how-to-get-the-path-of-a-running-jar-file + // Get path where the jar is located + File f = new File(ApplicationConfigurator.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()); + + // Check whether class was loaded from a jar file + if(f.isDirectory()){ + // Loaded from a directory + home = f.getParentFile().getParentFile(); + } + else{ + // Jar file + // Assume that the jar file is located in a /lib directory + home = f.getParentFile().getParentFile().getParentFile(); + } + } catch (URISyntaxException e) {; + } + } + return(home); + } + + /** + * Initialize fda home directory + * Will create the home directory directory structure + * + * @param home Home directory + */ + public void createApplicationHome(){ + + // Create directories + if(!home.exists()){ + logger.info("Create home directory: "+home.getAbsolutePath()); + home.mkdirs(); + } + + // Configuration directory + if(!configdir.exists()){ + logger.info("Create configuration directory: "+configdir.getAbsolutePath()); + configdir.mkdir(); + } + + // Data directory + if(!datadir.exists()){ + logger.info("Create data directory: "+datadir.getAbsolutePath()); + datadir.mkdir(); + } + + // Log directory + if(!logdir.exists()){ + logger.info("Create log directory: "+logdir.getAbsolutePath()); + logdir.mkdir(); + } + + // Scan directories + if(!scandir.exists()){ + logger.info("Create scan directory: "+scandir.getAbsolutePath()); + scandir.mkdir(); + } + File sdir = new File(scandir, "templates"); + if(!sdir.exists()){ + logger.info("Create template scan directory: "+sdir.getAbsolutePath()); + sdir.mkdir(); + } + sdir = new File(scandir, "users"); + if(!sdir.exists()){ + logger.info("Create user scan directory: "+sdir.getAbsolutePath()); + sdir.mkdir(); + } + + } + + /** + * Create required configuration files + */ + public void createDefaultConfigurationFiles() { + try{ + + if(!jcaeProperties.exists()){ + logger.info("Create jcae.properties file: "+jcaeProperties.getAbsolutePath()); + // jca.properties + String addresses = getLocalBroadcastAddresses(); + + BufferedWriter writer = new BufferedWriter(new FileWriter(jcaeProperties)); + PrintWriter w = new PrintWriter(writer); + // Set address list + w.println("ch.psi.jcae.ContextFactory.addressList="+addresses); + // Timeout for creating channels + w.println("ch.psi.jcae.ChannelFactory.timeout=2000"); + w.println("ch.psi.jcae.ChannelFactory.retries=4"); + // Get/set timeout of a ChannelBean + w.println("ch.psi.jcae.ChannelBeanFactory.timeout=10000"); + // Set default wait time for wait operations of a ChannelBean (30 minutes) + w.println("ch.psi.jcae.ChannelBeanFactory.waitTimeout=1800000"); + // Set wait retry period for wait operations of a ChannelBean (1 minutes) + w.println("ch.psi.jcae.ChannelBeanFactory.waitRetryPeriod=60000"); + // Set retries for operations on a ChannelBean + w.println("ch.psi.jcae.ChannelBeanFactory.retries=4"); + writer.close(); + } + + if(!fdaProperties.exists()){ + logger.info("Create fda.properties file: "+fdaProperties.getAbsolutePath()); + // fda.properties + BufferedWriter writer = new BufferedWriter(new FileWriter(fdaProperties)); + PrintWriter w = new PrintWriter(writer); + + // w.println("ch.psi.fda.aq.data.baseDirectory="+datadir.getAbsolutePath()); + w.println("ch.psi.fda.aq.data.filePrefix=${yyyy_MM}/${yyyyMMdd}/${yyyyMMddHHmmss}_${name}/${yyyyMMddHHmm}_"); + + w.println(); + w.println("# Global Error Notification (uncomment if needed)"); + w.println("#ch.psi.fda.aq.notification.recipients=mail.address@psi.ch smsnumber"); + + w.println(); + w.println("# OTFSCAN Configuration (uncomment if needed)"); + w.println("#ch.psi.fda.aq.otf.channelPrefix=X-X-OTFX"); + w.println("#ch.psi.fda.aq.otf.nfsServer=beamlineFileServer.psi.ch"); + w.println("#ch.psi.fda.aq.otf.nfsShare=/usr/local/nfsshare"); + w.println("#ch.psi.fda.aq.otf.smbShare=smb://:@beamlineFileServer.psi.ch/otftemp/"); + + writer.close(); + } + + if(!loggingProperties.exists()){ + logger.info("Create logging.properties file: "+loggingProperties.getAbsolutePath()); + // logging.properties + BufferedWriter writer = new BufferedWriter(new FileWriter(loggingProperties)); + PrintWriter w = new PrintWriter(writer); + + w.println("# Specify the handlers to create in the root logger"); + w.println("handlers = java.util.logging.ConsoleHandler, java.util.logging.FileHandler"); + w.println(""); + w.println("# Set the default logging level for the root logger"); + w.println(".level=INFO"); + w.println(""); + w.println("# Set the default logging level for new ConsoleHandler instances"); + w.println("java.util.logging.ConsoleHandler.level=ALL"); + w.println(""); + w.println("# Set the default formatter for new ConsoleHandler instances"); + w.println("java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter"); + w.println(""); + w.println("# Set the default logging level for new FileHandler instances"); + w.println("java.util.logging.FileHandler.level=ALL"); + w.println(""); + w.println("# Set the default formatter for new ConsoleHandler instances"); + w.println("java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter"); + w.println(""); + w.println("# Naming of the output file:"); + w.println("java.util.logging.FileHandler.pattern="+logdir.getAbsolutePath()+"/fda-%u.%g.log"); + w.println(""); + w.println("# Limiting size of output file in bytes (10000kb):"); + w.println("java.util.logging.FileHandler.limit=10000000"); + w.println(""); + w.println("# Number of output files to cycle through, by appending an"); + w.println("# integer to the base file name:"); + w.println("java.util.logging.FileHandler.count=10"); + w.println(""); + w.println("# Set the default logging level for the logger named com.mycompany"); + w.println("ch.psi.fda.level=ALL"); + writer.close(); + } + } + catch(IOException e){ + throw new RuntimeException("Cannot create default configuration files",e); + } + + } + + /** + * Determine local broadcast addresses of the available network interfaces + * @return + */ + private String getLocalBroadcastAddresses(){ + StringBuffer bAddressList = new StringBuffer(); + + // Loop host interfaces + Enumeration networkInterfaces; + try { + networkInterfaces = NetworkInterface.getNetworkInterfaces(); + } catch (SocketException e) { + // Something went wrong while determining the network interfaces of the current machine. + // As there is nothing a user can do, turn this exception into a unchecked RuntimeException. + throw new RuntimeException("Unable to determine network interfaces of the machine", e); + } + while (networkInterfaces.hasMoreElements()) { + NetworkInterface networkInterface = networkInterfaces.nextElement(); + + // Loop IP addresses of interface + List interfaceAddresses = networkInterface.getInterfaceAddresses(); + + for(InterfaceAddress interfaceAddress: interfaceAddresses){ + InetAddress broadcastAddress = interfaceAddress.getBroadcast(); + if (broadcastAddress != null) { + if(bAddressList.length()>0){ + bAddressList.append(" "); + } + // Use of .substring(1) to remove leading "/" character + bAddressList.append(broadcastAddress.toString().substring(1)); + } + } + } + return(bAddressList.toString()); + } +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/model/ModelManager.java b/ch.psi.fda/src/main/java/ch/psi/fda/model/ModelManager.java new file mode 100644 index 0000000..3e92aa6 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/model/ModelManager.java @@ -0,0 +1,178 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.model; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.UnmarshalException; +import javax.xml.bind.Unmarshaller; +import javax.xml.namespace.QName; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; + +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +import ch.psi.fda.model.v1.Configuration; + +/** + * Manage the serialization and deserialization of the model + * @author ebner + * + */ +public class ModelManager { + + /** + * Deserialize an instance of a data model of the type Configuration + * Before creating the object model variables will be replaced + * + * @param file Scan file + * @param variables Scan variables + * @throws JAXBException Something went wrong while unmarshalling + * @throws SAXException Cannot read model schema file + * @throws ParserConfigurationException + * @throws IOException + * @throws TransformerFactoryConfigurationError + * @throws TransformerException + */ + public static Configuration unmarshall(File file, HashMap variables) throws JAXBException, SAXException, IOException, + ParserConfigurationException, TransformerFactoryConfigurationError, TransformerException { + + // Load template file + Transformer xformer = TransformerFactory.newInstance().newTransformer(new StreamSource(file)); + // Overwrite parameters + for(String key: variables.keySet()){ + xformer.setParameter(key, variables.get(key)); + } + + + // Workaround for complex parameters (parameter including xml fragments) + // How the workaround works: + // Because we want to set complex parameters we first have to apply the xslt transformation to the input + // stream and create an other output stream. In the template file the complex parameter must be referenced as follows + // + // If it is not done this way the < and > of the tags will be replaced by < and > + // Also if we directly transform to a DOMResult the dom tree would not include the + // nodes added by the parameter but just a text object. Therefor we do the workaround via the StreamResult ... + ByteArrayOutputStream bstream = new ByteArrayOutputStream(); + StreamResult sresult = new StreamResult(bstream); + + + // Perform transformation (using template file also as input source) + xformer.transform(new StreamSource(file), sresult ); + + + // Workaround for complex parameters + ByteArrayInputStream bistream = new ByteArrayInputStream(bstream.toByteArray()); + StreamSource bsource = new StreamSource(bistream); + + + JAXBContext context = JAXBContext.newInstance(Configuration.class); + Unmarshaller u = context.createUnmarshaller(); + + // Validation + SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI); + Source s = new StreamSource(Configuration.class.getResourceAsStream("/model-v1.xsd")); + Schema schema = sf.newSchema(new Source[]{s}); // Use schema reference provided in XML + u.setSchema(schema); + + try{ + Configuration model = (Configuration) u.unmarshal(bsource, Configuration.class).getValue(); + return (model); + } + catch(UnmarshalException e){ + // Check + if(e.getLinkedException() instanceof SAXParseException){ + throw new RuntimeException("Configuration file does not comply to required model specification\nCause: "+e.getLinkedException().getMessage(), e); + } + throw e; + } + } + + + /** + * Deserialize an instance of a data model of the type Configuration + * @param file + * @throws JAXBException Something went wrong while unmarshalling + * @throws SAXException Cannot read model schema file + */ + public static Configuration unmarshall(File file) throws JAXBException, SAXException { + + JAXBContext context = JAXBContext.newInstance(Configuration.class); + Unmarshaller u = context.createUnmarshaller(); + + // Validation + SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI); + Source s = new StreamSource(Configuration.class.getResourceAsStream("/model-v1.xsd")); + Schema schema = sf.newSchema(new Source[]{s}); // Use schema reference provided in XML + u.setSchema(schema); + + try{ + Configuration model = (Configuration) u.unmarshal(new StreamSource(file), Configuration.class).getValue(); + return (model); + } + catch(UnmarshalException e){ + // Check + if(e.getLinkedException() instanceof SAXParseException){ + throw new RuntimeException("Configuration file does not comply to required model specification\nCause: "+e.getLinkedException().getMessage(), e); + } + throw e; + } + } + + /** + * Serialize an instance of a data model of the type Configuration + * @param model Model datastructure + * @param file File to write the model data into + * @throws JAXBException Something went wrong while marshalling model + * @throws SAXException Cannot read model schema files + */ + public static void marshall(Configuration model, File file) throws JAXBException, SAXException{ + QName qname = new QName("http://www.psi.ch/~ebner/models/scan/1.0", "configuration"); + + JAXBContext context = JAXBContext.newInstance(Configuration.class); + Marshaller m = context.createMarshaller(); + m.setProperty("jaxb.formatted.output", true); + + // Validation + SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI); + Source s = new StreamSource(Configuration.class.getResourceAsStream("/model-v1.xsd")); + Schema schema = sf.newSchema(new Source[]{s}); // Use schema reference provided in XML + m.setSchema(schema); + + m.marshal( new JAXBElement(qname, Configuration.class, model ), file); + } +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/notification/NotificationAgent.java b/ch.psi.fda/src/main/java/ch/psi/fda/notification/NotificationAgent.java new file mode 100644 index 0000000..eb76715 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/notification/NotificationAgent.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2011 Paul Scherrer Institute + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package ch.psi.fda.notification; + +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.mail.Transport; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; + +import ch.psi.fda.model.v1.Recipient; + +/** + * This is a copy of the NotificationAgent class that should go into the foundation classes + * This actually should be extracted to an own project + * @author ebner + */ +public class NotificationAgent { + + private final static String smsPostfix = "@sms.switch.ch"; + + private Properties properties; + private String fromAddress; + private final List recipients = new ArrayList(); + + /** + * Constructor + * @param host SMTP server to send notifications to + * @param recipients List of recipients + * @param from Address string that will show up in the from field. For example: fda@psi.ch. This argument must not contain white spaces + */ + public NotificationAgent(String host, String from){ + + fromAddress = from; + + properties = new Properties(); + // Setup mail server + properties.put("mail.smtp.host", host); + } + + + + public void sendNotification(String aSubject, String aBody, boolean error, boolean success) { + + for(Recipient recipient: recipients){ + + if((error && recipient.isError()) || (success && recipient.isSuccess())){ + String receiver; + + // Verify mail recipients + if(recipient.getValue().matches("[0-9,\\\\.,-,a-z,A-Z]*@[0-9,\\\\.,a-z,A-Z]*")){ + receiver = recipient.getValue(); + } + else if(recipient.getValue().matches("[0-9]+")){ + // Assume that it is a SMS number + receiver = recipient.getValue() + smsPostfix; + } + else{ + Logger.getLogger(NotificationAgent.class.getName()).log(Level.WARNING, "Invalid email address"); + continue; + } + + + Logger.getLogger(NotificationAgent.class.getName()).log(Level.INFO, "Send notification to " + receiver); + + //Here, no Authenticator argument is used (it is null). + //Authenticators are used to prompt the user for user + //name and password. + Session session = Session.getDefaultInstance(properties, null); + MimeMessage message = new MimeMessage(session); + try { + //The "from" address may be set in code, or set in the + //config file under "mail.from" ; here, the latter style is used + message.setFrom( new InternetAddress(fromAddress) ); + + message.addRecipient(Message.RecipientType.TO, new InternetAddress(receiver)); + message.setSubject(aSubject); + message.setText(aBody); + Transport.send(message); + } catch (MessagingException ex) { + Logger.getLogger(NotificationAgent.class.getName()).log(Level.WARNING, "Failed to send notification to " + receiver, ex); + } + } + } + } + + public List getRecipients() { + return recipients; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializer.java b/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializer.java new file mode 100644 index 0000000..cfd01d1 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializer.java @@ -0,0 +1,28 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.serializer; + +/** + * Data Serializer + * @author ebner + * + */ +public interface DataSerializer extends Runnable { +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerMAT.java b/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerMAT.java new file mode 100644 index 0000000..262a1b0 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerMAT.java @@ -0,0 +1,179 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.serializer; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import com.jmatio.io.MatFileWriter; +import com.jmatio.types.MLArray; +import com.jmatio.types.MLDouble; + +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; + +/** + * Serialize data received by a DataQueue into a Matlab file + * @author ebner + * + */ +public class DataSerializerMAT implements DataSerializer{ + + private DataQueue queue; + private File file; + + private boolean appendSuffix = false; + + /** + * Construtor + * @param queue Data queue holding the data to serialize + * @param file Name of the Matlab file to serialize the data to + */ + public DataSerializerMAT(DataQueue queue, File file){ + this.queue = queue; + this.file = file; + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + try{ + + // WORKAROUND BEGIN + File outfile; + if(appendSuffix){ + // Append a count suffix to the file. If there is already a file with + // this suffix increase the counter for the suffix + int cnt = 0; + String fname = this.file.getAbsolutePath(); // Determine file name + String extension = fname.replaceAll("^.*\\.", ""); // Determine extension + fname = fname.replaceAll("\\."+extension+"$", ""); + + outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension)); + + while(outfile.exists()){ + cnt++; + outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension)); + } + } + else{ + outfile = this.file; + } + // WORKAROUND END + + + // Transposed data list + List> dlist = new ArrayList>(); + List> clist = new ArrayList>(); + + boolean firstF = true; + + // Write data + // Read Message + Message message = queue.getQueue().take(); + while(!(message instanceof EndOfStreamMessage)){ + if(message instanceof DataMessage){ + DataMessage m = (DataMessage) message; + + // Initialize list + if(firstF){ + for(Object o: m.getData()){ + dlist.add(new ArrayList()); + clist.add(o.getClass()); + } + firstF=false; + } + + // Put data into data list + for(int i=0;i< m.getData().size();i++){ + Object object = m.getData().get(i); + dlist.get(i).add(object); + } + } + + // Read next message + message = queue.getQueue().take(); + } + + // Create Matlab vectors + ArrayList matlablist = new ArrayList(); + for(int t=0; t list = dlist.get(t); + + + + if(clist.get(t).isArray()){ + // Array Handling + } + else if(clist.get(t).equals(Double.class)){ + // Data is of type Double + MLDouble darray = new MLDouble(escapeString(c.getId()),(Double[])list.toArray(new Double[list.size()]),1); + matlablist.add(darray); + } + + + } + + // Write Matlab file + MatFileWriter writerr = new MatFileWriter(); + writerr.write(outfile, matlablist); + + + + } catch (InterruptedException e) { + // TODO Stop loop and exit logic instead of throwing an Exception + throw new RuntimeException("Data serializer was interrupted while writing data to file",e); + } catch (IOException e) { + throw new RuntimeException("Data serializer had a problem writing to the specified file",e); + } + + } + + /** + * Escape string to be Matlab key conform + * @param value + * @return Escaped string value + */ + private String escapeString(String value){ + + String evalue = value.replaceAll("-", "_"); + evalue = evalue.replaceAll(":", "_"); + evalue = evalue.replaceAll("\\.", "_"); + evalue = evalue.replaceAll(" ", "_"); + evalue = evalue.replaceAll("\\(", "_"); + evalue = evalue.replaceAll("\\)", "_"); + evalue = evalue.replaceAll("\\[", "_"); + evalue = evalue.replaceAll("\\]", "_"); + + return(evalue); + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerMAT2D.java b/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerMAT2D.java new file mode 100644 index 0000000..70f15c1 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerMAT2D.java @@ -0,0 +1,254 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.serializer; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +import com.jmatio.io.MatFileWriter; +import com.jmatio.types.MLArray; +import com.jmatio.types.MLDouble; + +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.StreamDelimiterMessage; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; + +/** + * Serialize data received by a DataQueue into a Matlab file + * @author ebner + * + */ +public class DataSerializerMAT2D implements DataSerializer{ + + // Get Logger + private static final Logger logger = Logger.getLogger(DataSerializerMAT2D.class.getName()); + + private DataQueue queue; + private File file; + private boolean appendSuffix = false; + + /** + * Construtor + * @param queue Data queue holding the data to serialize + * @param file Name of the Matlab file to serialize the data to + */ + public DataSerializerMAT2D(DataQueue queue, File file){ + this.queue = queue; + this.file = file; + + // Check if input queue does only hold 2D data + int maxdim=0; + for(ComponentMetadata m: queue.getDataMessageMetadata().getComponents()){ + if(m.getDimension()>maxdim){ + maxdim=m.getDimension(); + } + + if(m.getDimension()>1){ + throw new RuntimeException("Serializer does only support 2D data (XD data found)"); + } + } + + if(maxdim<1){ + throw new RuntimeException("Serializer does only support 2D data ("+maxdim+"D data found)"); + } + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + try{ + + // WORKAROUND BEGIN + File outfile; + if(appendSuffix){ + // Append a count suffix to the file. If there is already a file with + // this suffix increase the counter for the suffix + int cnt = 0; + String fname = this.file.getAbsolutePath(); // Determine file name + String extension = fname.replaceAll("^.*\\.", ""); // Determine extension + fname = fname.replaceAll("\\."+extension+"$", ""); + + outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension)); + + while(outfile.exists()){ + cnt++; + outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension)); + } + } + else{ + outfile = this.file; + } + // WORKAROUND END + + + // Transposed data list + List>> dlist = new ArrayList>>(); + List> clist = new ArrayList>(); + int dsize = 0; // Size of the dimension + int dcount = 0; + Integer mindsize = null; + + boolean firstF = true; + + // Write data + // Read Message + Message message = queue.getQueue().take(); + while(!(message instanceof EndOfStreamMessage)){ + if(message instanceof DataMessage){ + DataMessage m = (DataMessage) message; + + // Initialize list + if(firstF){ + for(Object o: m.getData()){ + // TODO Create list of lists (for each line one list - to + // be able to fill up empty data points if there are some) + List> l = new ArrayList>(); + l.add(new ArrayList()); + dlist.add(l); + + clist.add(o.getClass()); + } + firstF=false; + } + + // Put data into data list + for(int i=0;i< m.getData().size();i++){ + Object object = m.getData().get(i); + List> lo = dlist.get(i); + lo.get(lo.size()-1).add(object); // add data to latest list + } + + dcount++; + } + else if(message instanceof StreamDelimiterMessage){ + StreamDelimiterMessage m = (StreamDelimiterMessage) message; + if(m.getNumber()==0){ + // Determine minimum dimension size + if(dsizedcount){ + mindsize=dcount; + } + + // Add a new list for all component to the dlist + for(List> lo:dlist){ + lo.add(new ArrayList()); + } + + dcount=0; + } + } + + // Read next message + message = queue.getQueue().take(); + } + + logger.info("dsize: "+dsize + " mindsize:"+mindsize); + + // Create Matlab vectors + ArrayList matlablist = new ArrayList(); + logger.info("dlist size: "+dlist.size()); + for(int t=0; t list = new ArrayList(); + List> ol = dlist.get(t); + + // Remove last array list as it is empty + ol.remove(ol.size()-1); + + for(List li: ol){ + list.addAll(li); + // Pad list if there are missing data points for some lines + for(int i=li.size();i list = dlist.get(t); + logger.info("List: "+list.size()); + + + if(clist.get(t).isArray()){ + // Array Handling + } + else if(clist.get(t).equals(Double.class)){ + // Data is of type Double + MLDouble darray = new MLDouble(escapeString(c.getId()),(Double[])list.toArray(new Double[list.size()]), dsize); + matlablist.add(darray); + } + + + } + + // Write Matlab file + MatFileWriter writerr = new MatFileWriter(); + writerr.write(outfile, matlablist); + + + + } catch (InterruptedException e) { + // TODO Stop loop and exit logic instead of throwing an Exception + throw new RuntimeException("Data serializer was interrupted while writing data to file",e); + } catch (IOException e) { + throw new RuntimeException("Data serializer had a problem writing to the specified file",e); + } + + } + + /** + * Escape string to be Matlab key conform + * @param value + * @return Escaped string value + */ + private String escapeString(String value){ + + String evalue = value.replaceAll("-", "_"); + evalue = evalue.replaceAll(":", "_"); + evalue = evalue.replaceAll("\\.", "_"); + evalue = evalue.replaceAll(" ", "_"); + evalue = evalue.replaceAll("\\(", "_"); + evalue = evalue.replaceAll("\\)", "_"); + evalue = evalue.replaceAll("\\[", "_"); + evalue = evalue.replaceAll("\\]", "_"); + + return(evalue); + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerMAT2DZigZag.java b/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerMAT2DZigZag.java new file mode 100644 index 0000000..58147fd --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerMAT2DZigZag.java @@ -0,0 +1,235 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.serializer; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Logger; + +import com.jmatio.io.MatFileWriter; +import com.jmatio.types.MLArray; +import com.jmatio.types.MLDouble; + +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.StreamDelimiterMessage; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; + +/** + * Serialize data received by a DataQueue into a Matlab file + * @author ebner + * + */ +public class DataSerializerMAT2DZigZag implements DataSerializer{ + + // Get Logger + private static final Logger logger = Logger.getLogger(DataSerializerMAT2DZigZag.class.getName()); + + private DataQueue queue; + private File file; + private boolean appendSuffix = false; + + /** + * Construtor + * @param queue Data queue holding the data to serialize + * @param file Name of the Matlab file to serialize the data to + */ + public DataSerializerMAT2DZigZag(DataQueue queue, File file){ + this.queue = queue; + this.file = file; + + // Check if input queue does only hold 2D data + int maxdim=0; + for(ComponentMetadata m: queue.getDataMessageMetadata().getComponents()){ + if(m.getDimension()>maxdim){ + maxdim=m.getDimension(); + } + + if(m.getDimension()>1){ + throw new RuntimeException("Serializer does only support 2D data (XD data found)"); + } + } + + if(maxdim<1){ + throw new RuntimeException("Serializer does only support 2D data ("+maxdim+"D data found)"); + } + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + try{ + + // WORKAROUND BEGIN + File outfile; + if(appendSuffix){ + // Append a count suffix to the file. If there is already a file with + // this suffix increase the counter for the suffix + int cnt = 0; + String fname = this.file.getAbsolutePath(); // Determine file name + String extension = fname.replaceAll("^.*\\.", ""); // Determine extension + fname = fname.replaceAll("\\."+extension+"$", ""); + + outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension)); + + while(outfile.exists()){ + cnt++; + outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension)); + } + } + else{ + outfile = this.file; + } + // WORKAROUND END + + + // Transposed data list + List> dlist = new ArrayList>(); + List> dlistTmp = new ArrayList>(); + List> clist = new ArrayList>(); + int dsize = 0; // Size of the dimension + int dcount = 0; + + int delimiterCount = 0; + + boolean firstF = true; + boolean firstC = true; + + // Write data + // Read Message + Message message = queue.getQueue().take(); + while(!(message instanceof EndOfStreamMessage)){ + if(message instanceof DataMessage){ + DataMessage m = (DataMessage) message; + + if(firstC){ + for(Object o: m.getData()){ + dlist.add(new ArrayList()); + clist.add(o.getClass()); + firstC=false; + } + } + + // Initialize list + if(firstF){ + for(int i=0;i()); + } + firstF=false; + } + + // Put data into data list + for(int i=0;i< m.getData().size();i++){ + Object object = m.getData().get(i); + dlistTmp.get(i).add(object); + } + + dcount++; + } + else if(message instanceof StreamDelimiterMessage){ + StreamDelimiterMessage m = (StreamDelimiterMessage) message; + if(m.getNumber()==0){ + if(dsize matlablist = new ArrayList(); + logger.info("dlist size: "+dlist.size()); + for(int t=0; t list = dlist.get(t); + + if(clist.get(t).isArray()){ + // Array Handling + } + else if(clist.get(t).equals(Double.class)){ + // Data is of type Double + MLDouble darray = new MLDouble(escapeString(c.getId()),(Double[])list.toArray(new Double[list.size()]), dsize); + matlablist.add(darray); + } + + + } + + // Write Matlab file + MatFileWriter writerr = new MatFileWriter(); + writerr.write(outfile, matlablist); + + + + } catch (InterruptedException e) { + // TODO Stop loop and exit logic instead of throwing an Exception + throw new RuntimeException("Data serializer was interrupted while writing data to file",e); + } catch (IOException e) { + throw new RuntimeException("Data serializer had a problem writing to the specified file",e); + } + + } + + /** + * Escape string to be Matlab key conform + * @param value + * @return Escaped string value + */ + private String escapeString(String value){ + + String evalue = value.replaceAll("-", "_"); + evalue = evalue.replaceAll(":", "_"); + evalue = evalue.replaceAll("\\.", "_"); + evalue = evalue.replaceAll(" ", "_"); + evalue = evalue.replaceAll("\\(", "_"); + evalue = evalue.replaceAll("\\)", "_"); + evalue = evalue.replaceAll("\\[", "_"); + evalue = evalue.replaceAll("\\]", "_"); + + return(evalue); + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerMDA.java b/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerMDA.java new file mode 100644 index 0000000..af2a3de --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerMDA.java @@ -0,0 +1,392 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.serializer; + +import hep.io.xdr.XDRRandomAccessFile; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.logging.Logger; + +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; +import ch.psi.fda.core.messages.StreamDelimiterMessage; + +/** + * Serialize data received by a DataQueue + * + * http://www.aps.anl.gov/bcda/synApps/sscan/saveData_fileFormat.txt + * + * @author ebner + * + */ +public class DataSerializerMDA implements DataSerializer{ + + + // Get Logger + private static final Logger logger = Logger.getLogger(DataSerializerMDA.class.getName()); + + private DataQueue queue; + private File file; + + public DataSerializerMDA(DataQueue queue, File file){ + this.queue = queue; + this.file = file; + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + try{ + + + + // Analyze header + + // Map holding all indexes for a given dimension + HashMap> dMap = new HashMap>(); + // Map holding all ids for a given dimension + HashMap> idMap = new HashMap>(); + + List mlist = queue.getDataMessageMetadata().getComponents(); + for(int index=0;index()); + } + if(!idMap.containsKey(m.getDimension())){ + idMap.put(m.getDimension(), new ArrayList()); + } + dMap.get(m.getDimension()).add(index); + idMap.get(m.getDimension()).add(m.getId()); + } + + //dimensions/dimension/dimensioncomponents/component/componentvalue + List>>> dimensionList = new ArrayList>>>(); + + int numberOfDimensions = dMap.size(); + logger.info("Number of dimensions: "+numberOfDimensions); + for(int i=0;i>>()); + } + + +// // Transposed data list +// List> dlist = new ArrayList>(); +//// List> clist = new ArrayList>(); +// int dsize = 0; // Size of the dimension +// int dcount = 0; +// boolean firstF = true; + + List firstL = new ArrayList(); + List takeData = new ArrayList(); // Flag whether to take data for this dimension + List dcountL = new ArrayList(); // How many times this dimension is there + + for(int i=0;i> l = new ArrayList>(); + dimensionList.get(dimensionNumberKey).add(l); + + for(int y=0;y()); + } + + // Set first flag to false + firstL.set(dimensionNumberKey, false); + + } + + // Read data from data message + if(takeData.get(dimensionNumberKey) || dimensionNumberKey == 0){ + for(int y=0;y=0; i--){ + int s = dimensionList.get(i).get(0).get(0).size(); + x.writeInt(s); // Dimension size + logger.info("Size: "+i+" - "+s+" "); + } + + x.writeInt(1); // Is Regular (true=1, false=0) + x.writeInt(0); // Number of extra pvs + + // Write data + HashMap indexCount = new HashMap(); + for(int i=0;i>>> dimensionList, HashMap icount, HashMap> idMap, int dnum) throws IOException{ + + int ic = icount.get(dnum); + List> l = dimensionList.get(dnum).get(ic); + // Increment count for dimension + icount.put(dnum,(ic+1)); + + logger.info("Write data: "+dnum+"["+ic+"]"); + + // Write data to file + // Address of this dimension (block) + long daddress = x.getFilePointer(); + List dpaddressL = writeData(x, l, idMap.get(dnum), dnum); + + + if(dnum>0){ + for(int i=0;i writeData(XDRRandomAccessFile x, List> list, List ids, int dimension) throws IOException{ + int npoints = list.get(0).size(); + + logger.info("Dimension rank: "+(dimension+1)); + x.writeInt(dimension+1); // Scan rank + x.writeInt(npoints); // Number of points + x.writeInt(npoints); // Current Point + + // Address of the place where the pointers to the lower level dimensions + // are stored. + List dpaddressList = new ArrayList(); +// long dpaddress = x.getFilePointer(); + // Read pointers to lower scans + if(dimension > 0){ + // For 1D scans this block is never reached + for(int i=0;i. + * + */ + +package ch.psi.fda.serializer; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.logging.Logger; + +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; +import ch.psi.fda.core.messages.StreamDelimiterMessage; + +/** + * Serialize data received by a DataQueue + * @author ebner + * + */ +public class DataSerializerTXT implements DataSerializer{ + + + // Get Logger + private static final Logger logger = Logger.getLogger(DataSerializerTXT.class.getName()); + + private DataQueue queue; + private File file; + + private boolean appendSuffix = true; + + /** + * + * @param queue + * @param file + * @param appendSuffix Flag whether to append a _0000 suffix after the original file name + */ + public DataSerializerTXT(DataQueue queue, File file, boolean appendSuffix){ + this.queue = queue; + this.file = file; + this.appendSuffix = appendSuffix; + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + try{ + + // WORKAROUND BEGIN + File outfile; +// if(appendSuffix){ +// // Append a count suffix to the file. If there is already a file with +// // this suffix increase the counter for the suffix +// int cnt = 0; +// String fname = this.file.getAbsolutePath(); // Determine file name +// String extension = fname.replaceAll("^.*\\.", ""); // Determine extension +// fname = fname.replaceAll("\\."+extension+"$", ""); +// +// outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension)); +// +// while(outfile.exists()){ +// cnt++; +// outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension)); +// } +// } +// else{ +// outfile = this.file; +// } + // WORKAROUND END + + + + + + // Write header + StringBuffer b = new StringBuffer(); + StringBuffer b1 = new StringBuffer(); + b.append("#"); + b1.append("#"); + for(ComponentMetadata c: queue.getDataMessageMetadata().getComponents()){ + + b.append(c.getId()); + b.append("\t"); + + b1.append(c.getDimension()); + b1.append("\t"); + } + b.setCharAt(b.length()-1, '\n'); + b1.setCharAt(b1.length()-1, '\n'); + + + int icount = 0; + boolean newfile = true; + boolean dataInBetween = false; + BufferedWriter writer = null; + + // Get basename of the file + String basename = this.file.getAbsolutePath(); // Determine file name + String extension = basename.replaceAll("^.*\\.", ""); // Determine extension + basename = basename.replaceAll("\\."+extension+"$", ""); + + // Write data + // Read Message + Message message = queue.getQueue().take(); + while(!(message instanceof EndOfStreamMessage)){ + if(message instanceof DataMessage){ + dataInBetween = true; + if(newfile){ + // Open new file and write header + // Construct file name + if(appendSuffix){ + outfile = new File(String.format("%s_%04d.%s", basename, icount, extension)); + } + else{ + outfile = new File(String.format("%s.%s", basename, extension)); + } + + // Open file + logger.fine("Open new data file: "+outfile.getAbsolutePath()); + writer = new BufferedWriter(new FileWriter(outfile)); + + // Write header + writer.write(b.toString()); + writer.write(b1.toString()); + + newfile=false; + } + + // Write message to file - each message will result in one line + DataMessage m = (DataMessage) message; + StringBuffer buffer = new StringBuffer(); + for(Object o: m.getData()){ + if(o.getClass().isArray()){ + // If the array object is of type double[] display its content + if(o instanceof double[]){ + double[] oa = (double[]) o; + for(double o1 : oa){ + buffer.append(o1); + buffer.append(" "); // Use space instead of tab + } + buffer.replace(buffer.length()-1,buffer.length()-1 , "\t"); // Replace last space with tab + } + else if(o instanceof Object[]){ + // TODO need to be recursive ... + Object[] oa = (Object[])o; + for(Object o1 : oa){ + buffer.append(o1); + buffer.append(" "); // Use space instead of tab + } + buffer.replace(buffer.length()-1,buffer.length()-1 , "\t"); // Replace last space with tab + } + else{ + buffer.append("-"); // Not supported + } + } + else{ + buffer.append(o); + buffer.append("\t"); + } + } + + if(buffer.length()>0){ + buffer.deleteCharAt(buffer.length()-1); // Remove last character (i.e. \t) + buffer.append("\n"); // Append newline + } + writer.write(buffer.toString()); + } + else if(message instanceof StreamDelimiterMessage){ + StreamDelimiterMessage m = (StreamDelimiterMessage) message; + logger.info("Delimiter - number: "+m.getNumber()+" iflag: "+m.isIflag()); + if(m.isIflag() && appendSuffix){ + // Only increase iflag counter if there was data in between + // subsequent StreamDelimiterMessages. + if(dataInBetween){ + icount++; + } + dataInBetween = false; + + // Set flag to open new file + newfile = true; + + // Close file + writer.close(); + } + } + + // Read next message + message = queue.getQueue().take(); + } + + if(writer!=null){ + // Close file + writer.close(); //If the stream was closed previously this has no effect + } + // Writer can be null if a scan is defined without a dimension + + } catch (InterruptedException e) { + // TODO Stop loop and exit logic instead of throwing an Exception + throw new RuntimeException("Data serializer was interrupted while writing data to file",e); + } catch (IOException e) { + throw new RuntimeException("Data serializer had a problem writing to the specified file",e); + } + + } + + +// /** +// * Enable/disable the generation of the _0000 suffix in front of the extension of the out file +// * @param appendSuffix the appendSuffix to set +// */ +// public void setAppendSuffix(boolean appendSuffix) { +// this.appendSuffix = appendSuffix; +// } +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerTXT2D.java b/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerTXT2D.java new file mode 100644 index 0000000..bf24aae --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerTXT2D.java @@ -0,0 +1,205 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.serializer; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.StreamDelimiterMessage; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; + +/** + * Serialize data received by a DataQueue into a Matlab file + * @author ebner + * + */ +public class DataSerializerTXT2D implements DataSerializer{ + + private DataQueue queue; + private File file; + private boolean appendSuffix = false; + + /** + * Construtor + * @param queue Data queue holding the data to serialize + * @param file Name of the Matlab file to serialize the data to + */ + public DataSerializerTXT2D(DataQueue queue, File file){ + this.queue = queue; + this.file = file; + + // Check if input queue does only hold 2D data + int maxdim=0; + for(ComponentMetadata m: queue.getDataMessageMetadata().getComponents()){ + if(m.getDimension()>maxdim){ + maxdim=m.getDimension(); + } + + if(m.getDimension()>1){ + throw new RuntimeException("Serializer does only support 2D data (XD data found)"); + } + } + + if(maxdim<1){ + throw new RuntimeException("Serializer does only support 2D data ("+maxdim+"D data found)"); + } + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + try{ + + // WORKAROUND BEGIN + File outfile; + if(appendSuffix){ + // Append a count suffix to the file. If there is already a file with + // this suffix increase the counter for the suffix + int cnt = 0; + String fname = this.file.getAbsolutePath(); // Determine file name + String extension = fname.replaceAll("^.*\\.", ""); // Determine extension + // fname = fname.replaceAll("(_[0-9]+)?\\."+extension+"$", ""); + fname = fname.replaceAll("\\."+extension+"$", ""); + + outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension)); + + while(outfile.exists()){ + cnt++; + outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension)); + } + } + else{ + outfile = this.file; + } + // WORKAROUND END + + + // Transposed data list + List> dlist = new ArrayList>(); + List> clist = new ArrayList>(); + int dsize = 0; // Size of the dimension + int dcount = 0; + + boolean firstF = true; + + // Write data + // Read Message + Message message = queue.getQueue().take(); + while(!(message instanceof EndOfStreamMessage)){ + if(message instanceof DataMessage){ + DataMessage m = (DataMessage) message; + + // Initialize list + if(firstF){ + for(Object o: m.getData()){ + dlist.add(new ArrayList()); + clist.add(o.getClass()); + } + firstF=false; + } + + // Put data into data list + for(int i=0;i< m.getData().size();i++){ + Object object = m.getData().get(i); + dlist.get(i).add(object); + } + + dcount++; + } + else if(message instanceof StreamDelimiterMessage){ + StreamDelimiterMessage m = (StreamDelimiterMessage) message; + if(m.getNumber()==0){ + if(dsize list = dlist.get(t); + + + + if(clist.get(t).isArray()){ + // Array Handling + } + else if(clist.get(t).equals(Double.class)){ + // Data is of type Double + + StringBuffer b = new StringBuffer(); + int counter = 0; + for(Object o: list){ + b.append(o); + counter++; + if(counter==dsize){ + b.append("\n"); + counter=0; + } + else{ + b.append(" "); + } + } + + writer.write(b.toString()); + } + + writer.write("\n"); + + } + + // Close file + writer.close(); + + + + } catch (InterruptedException e) { + // TODO Stop loop and exit logic instead of throwing an Exception + throw new RuntimeException("Data serializer was interrupted while writing data to file",e); + } catch (IOException e) { + throw new RuntimeException("Data serializer had a problem writing to the specified file",e); + } + + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerTXTSplit.java b/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerTXTSplit.java new file mode 100644 index 0000000..444696f --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/serializer/DataSerializerTXTSplit.java @@ -0,0 +1,240 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.serializer; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.StreamDelimiterMessage; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; + +/** + * Serialize data received by a DataQueue + * @author ebner + * + */ +public class DataSerializerTXTSplit implements DataSerializer{ + + private DataQueue queue; + private File file; + + private int maxdim = 0; + +// private boolean appendSuffix = false; + + public DataSerializerTXTSplit(DataQueue queue, File file){ + this.queue = queue; + this.file = file; + + + // Determine maximum dimension + for(ComponentMetadata m: queue.getDataMessageMetadata().getComponents()){ + if(m.getDimension()>maxdim){ + maxdim=m.getDimension(); + } + } + + if(maxdim<1){ + throw new RuntimeException("Split serializer only supports data > 1 dimension"); + } + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + try{ + + // WORKAROUND BEGIN +// File outfile; +// if(appendSuffix){ +// // Append a count suffix to the file. If there is already a file with +// // this suffix increase the counter for the suffix +// int cnt = 0; +// String fname = this.file.getAbsolutePath(); // Determine file name +// String extension = fname.replaceAll("^.*\\.", ""); // Determine extension +// fname = fname.replaceAll("\\."+extension+"$", ""); +// +// outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension)); +// +// while(outfile.exists()){ +// cnt++; +// outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension)); +// } +// } +// else{ +// outfile = this.file; +// } + // WORKAROUND END + + + + List header = new ArrayList(); + + // Write header + StringBuffer b = new StringBuffer(); + StringBuffer b1 = new StringBuffer(); + b.append("#"); + b1.append("#"); + for(ComponentMetadata c: queue.getDataMessageMetadata().getComponents()){ + + b.append(c.getId()); + b.append("\t"); + + b1.append(c.getDimension()); + b1.append("\t"); + } + b.setCharAt(b.length()-1, '\n'); + b1.setCharAt(b1.length()-1, '\n'); + header.add(b.toString()); + header.add(b1.toString()); + + List data = new ArrayList(); + // Write data + // Read Message + Message message = queue.getQueue().take(); + while(!(message instanceof EndOfStreamMessage)){ + if(message instanceof DataMessage){ + + // Write message to file - each message will result in one line + DataMessage m = (DataMessage) message; + StringBuffer buffer = new StringBuffer(); + for(Object o: m.getData()){ + if(o.getClass().isArray()){ + // If the array object is of type double[] display its content + if(o instanceof double[]){ + double[] oa = (double[]) o; + for(double o1 : oa){ + buffer.append(o1); + buffer.append(" "); // Use space instead of tab + } + buffer.replace(buffer.length()-1,buffer.length()-1 , "\t"); // Replace last space with tab + } + else if(o instanceof Object[]){ + // TODO need to be recursive ... + Object[] oa = (Object[])o; + for(Object o1 : oa){ + buffer.append(o1); + buffer.append(" "); // Use space instead of tab + } + buffer.replace(buffer.length()-1,buffer.length()-1 , "\t"); // Replace last space with tab + } + else{ + buffer.append("-"); // Not supported + } + } + else{ + buffer.append(o); + buffer.append("\t"); + } + } + + buffer.deleteCharAt(buffer.length()-1); // Remove last character (i.e. \t) + buffer.append("\n"); // Append newline + data.add(buffer.toString()); + } + else if(message instanceof StreamDelimiterMessage){ + StreamDelimiterMessage m = (StreamDelimiterMessage) message; + if(m.getNumber()==maxdim-1){ + writeData(header, data); + // Clear data + data.clear(); + } + } + + // Read next message + message = queue.getQueue().take(); + } + +// // Open file +// BufferedWriter writer = new BufferedWriter(new FileWriter(outfile)); +// +// // Close file +// writer.close(); + + } catch (InterruptedException e) { + // TODO Stop loop and exit logic instead of throwing an Exception + throw new RuntimeException("Data serializer was interrupted while writing data to file",e); + } catch (IOException e) { + throw new RuntimeException("Data serializer had a problem writing to the specified file",e); + } + + } + + private void writeData(List header, List data) throws IOException{ + + // WORKAROUND BEGIN + File outfile; +// if(appendSuffix){ + // Append a count suffix to the file. If there is already a file with + // this suffix increase the counter for the suffix + int cnt = 0; + String fname = this.file.getAbsolutePath(); // Determine file name + String extension = fname.replaceAll("^.*\\.", ""); // Determine extension + fname = fname.replaceAll("\\."+extension+"$", ""); + + outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension)); + + while(outfile.exists()){ + cnt++; + outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension)); + } +// } +// else{ +// outfile = this.file; +// } + // WORKAROUND END + + // Open file + BufferedWriter writer = new BufferedWriter(new FileWriter(outfile)); + + // Write header + for(String s: header){ + writer.write(s); + } + + // Write data + for(String s: data){ + writer.write(s); + } + + + // Close file + writer.close(); + } + + + /** + * Enable/disable the generation of the _0000 suffix in front of the extension of the out file + * @param appendSuffix the appendSuffix to set + */ + public void setAppendSuffix(boolean appendSuffix) { +// this.appendSuffix = appendSuffix; + } +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/vis/VisualizationEngine.java b/ch.psi.fda/src/main/java/ch/psi/fda/vis/VisualizationEngine.java new file mode 100644 index 0000000..514b58e --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/vis/VisualizationEngine.java @@ -0,0 +1,229 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.vis; + +import java.awt.FlowLayout; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.PrintWriter; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTabbedPane; +import javax.swing.ScrollPaneLayout; +import javax.xml.bind.JAXBException; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.GnuParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.OptionBuilder; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.xml.sax.SAXException; + +import ch.psi.fda.deserializer.DataDeserializer; +import ch.psi.fda.deserializer.DataDeserializerTXT; +import ch.psi.fda.gui.ScrollableFlowPanel; +import ch.psi.fda.model.ModelManager; +import ch.psi.fda.model.v1.Configuration; +import ch.psi.fda.visualizer.Visualizer; + +/** + * Visualize data according to the scan description + * + * @author ebner + * + */ +public class VisualizationEngine { + + // Get Logger + private static Logger logger = Logger.getLogger(VisualizationEngine.class.getName()); + + /** + * Default constructor + */ + public VisualizationEngine(){ + } + + /** + * Visualize data based on the configuration file + * @param configuration + * @param data + * @throws InterruptedException + * @throws SAXException + * @throws JAXBException + */ + public void visualize(File configuration, File data) throws InterruptedException{ + + if(configuration==null){ + throw new IllegalArgumentException("Configuration file is null"); + } + else if(!configuration.exists()){ + throw new IllegalArgumentException("Configuration file ["+configuration.getAbsolutePath()+"] does not exist"); + } + + // Unmarshall configuration file + Configuration c; + try { + c = ModelManager.unmarshall(configuration); + } catch (Exception e) { + throw new RuntimeException("Unable to load configuration file "+configuration.getAbsolutePath(), e); + } + + // Visualize data + visualize(c, data); + } + + /** + * Visualize data + * @param configuration + * @param data + * @throws InterruptedException + */ + public void visualize(Configuration configuration, File data) throws InterruptedException{ + + if(data==null){ + throw new IllegalArgumentException("Data file is null"); + } + else if(!data.exists()){ + throw new IllegalArgumentException("Data file ["+data.getAbsolutePath()+"] does not exist"); + } + + // Create deserializer + DataDeserializer deserializer = new DataDeserializerTXT(data); + + // Create Visualizer + Visualizer visualizer = new Visualizer(deserializer.getQueue(), configuration.getVisualization()); + + visualizer.setTerminateAtEOS(true); + // Adapt default visualizer behavior to optimize performance for visualization + visualizer.setUpdateAtStreamElement(false); + visualizer.setUpdateAtStreamDelimiter(false); + visualizer.setUpdateAtEndOfStream(true); + + JPanel opanel = new ScrollableFlowPanel(); + opanel.setLayout(new FlowLayout()); + + JScrollPane spane = new JScrollPane(opanel, ScrollPaneLayout.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneLayout.HORIZONTAL_SCROLLBAR_NEVER); + JTabbedPane tpane = new JTabbedPane(); + tpane.addTab("Overview", spane); + + for (JPanel p : visualizer.getPlotPanels()) { + opanel.add(p); + } + + final JFrame frame = new JFrame(); + frame.setSize(1200,800); + frame.add(tpane); +// frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + frame.addWindowListener(new WindowAdapter(){ + @Override + public void windowClosing(WindowEvent we){ + System.exit(0); + } + }); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);//.DO_NOTHING_ON_CLOSE); + + java.awt.EventQueue.invokeLater(new Runnable() { + public void run() { + frame.setVisible(true); + } + }); + + // Start deserializer and visualizer + + Thread td = new Thread(deserializer); + + td.start(); + visualizer.startVisualization(); + + td.join(); + logger.info("Deserializer finished"); +// visualizer.stopVisualization(); + } + + /** + * This method accepts a data file and an option (-file) specifying the scan configuration file + * used for creating the data file. If no option is specified the configuration file is derived by the + * data file name. + * + * @param args Command line arguments + */ + public static void main(String[] args) { + String scriptname = "fda_vis"; + File dfile = null; + File cfile = null; + + OptionBuilder.hasArg(); + OptionBuilder.withArgName("file"); + OptionBuilder.withDescription("Scan configuration file"); + OptionBuilder.withType(new String()); + Option o_cfile = OptionBuilder.create( "file"); + + Options options = new Options(); + options.addOption(o_cfile); + + CommandLineParser parser = new GnuParser(); + // Parse the command line arguments + try { + CommandLine line = parser.parse( options, args ); + + // Check whether exactly one file is specified + if(line.getArgs().length!=1){ + throw new ParseException("Only up to one argument is supported"); + } + dfile= new File(line.getArgs()[0]); + + if( line.hasOption(o_cfile.getOpt() )){ + cfile = new File(line.getOptionValue(o_cfile.getOpt())); + } + else{ + // Try to determine configuration file from data file name + File dir = dfile.getParentFile(); + String name = dfile.getName(); + name = name.replaceAll("_[0-9]*.txt$", ""); + cfile = new File(dir, name+".xml"); + } + + } catch (ParseException e) { + System.err.println(e.getMessage()); + HelpFormatter formatter = new HelpFormatter(); + formatter.printUsage(new PrintWriter(System.out, true), HelpFormatter.DEFAULT_WIDTH, scriptname, options); + System.exit(-1); + } + + VisualizationEngine e = new VisualizationEngine(); + try{ + e.visualize(cfile, dfile); + } + catch(Exception ee){ + System.out.println("Visualization failed due to: "+ee.getMessage()); + logger.log(Level.WARNING, "Visualization failed due to Exception:", ee); + System.exit(-1); + } + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/DataVisualization.java b/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/DataVisualization.java new file mode 100644 index 0000000..920a1b7 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/DataVisualization.java @@ -0,0 +1,55 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.visualizer; + +import java.util.ArrayList; +import java.util.List; + +import ch.psi.plot.Plot; + +/** + * @author ebner + * + */ +public class DataVisualization { + + private final Plot plot; + private final List series; + + public DataVisualization(Plot plot){ + this.plot = plot; + this.series = new ArrayList(); + } + + /** + * @return the plot + */ + public Plot getPlot() { + return plot; + } + + /** + * @return the series + */ + public List getSeries() { + return series; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/FilterSet.java b/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/FilterSet.java new file mode 100644 index 0000000..8070f66 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/FilterSet.java @@ -0,0 +1,74 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.visualizer; + +import java.util.ArrayList; +import java.util.List; + +import ch.psi.fda.core.messages.DataQueue; + +/** + * @author ebner + * + */ +public class FilterSet { + + private DataQueue queue; + private List filters = new ArrayList(); + private String name; + + public FilterSet(DataQueue queue){ + this("", queue); + } + + public FilterSet(String name, DataQueue queue){ + this.queue = queue; + } + + /** + * @return the queue + */ + public DataQueue getQueue() { + return queue; + } + + /** + * @return the filters + */ + public List getFilters() { + return filters; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/SeriesDataFilter.java b/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/SeriesDataFilter.java new file mode 100644 index 0000000..ada16ca --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/SeriesDataFilter.java @@ -0,0 +1,28 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.visualizer; + +/** + * @author ebner + * + */ +public interface SeriesDataFilter { + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/Visualizer.java b/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/Visualizer.java new file mode 100644 index 0000000..d420182 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/Visualizer.java @@ -0,0 +1,661 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.visualizer; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +//import org.jfree.data.xy.XYSeries; + +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.StreamDelimiterMessage; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; +import ch.psi.fda.model.v1.ArrayDetector; +import ch.psi.fda.model.v1.ContinuousPositioner; +import ch.psi.fda.model.v1.Detector; +import ch.psi.fda.model.v1.LinearPositioner; +import ch.psi.fda.model.v1.Positioner; +import ch.psi.fda.model.v1.PseudoPositioner; +import ch.psi.fda.model.v1.Visualization; +import ch.psi.plot.Plot; +import ch.psi.plot.xy.LinePlot; +import ch.psi.plot.xy.XYSeriesCollectionP; +import ch.psi.plot.xy.XYSeriesP; +//import ch.psi.plot.xyz.JFreeMatrixPlot; +//import ch.psi.plot.xyz.JFreeMatrixPlotData; +//import ch.psi.plot.xyz.JFreeMatrixPlotMetadata; +import ch.psi.plot.xyz.MatrixPlot; +import ch.psi.plot.xyz.MatrixPlotData; + +/** + * Serialize data received by a DataQueue + * @author ebner + * + */ +public class Visualizer { + + // Logger + private static Logger logger = Logger.getLogger(Visualizer.class.getName()); + + private FilterSet filterSet = null; + private List plots = new ArrayList(); + + private Thread visualizationThread = null; + + /** + * Terminate at end of stream + */ + private boolean terminateAtEOS = false; + private volatile boolean terminate = false; + + + private boolean updateAtStreamElement = true; + private boolean updateAtStreamDelimiter = true; + private boolean updateAtEndOfStream = false; + + public Visualizer(DataQueue queue, List vl){ + addFilterSet(queue, vl); + } + + private void addFilterSet(DataQueue queue, List vl){ + filterSet = mapVisualizations(queue, vl); + } + + + /** + * Visualize data + * Method blocks until visualization is done + */ + public void visualize() { + if(filterSet != null ){ + DataQueue queue = filterSet.getQueue(); + List filters = filterSet.getFilters(); + + int ecount = 0; + boolean clearPlot = false; + terminate = false; + + // Read Messages + Message message = null; + try { + message = queue.getQueue().take(); + } catch (InterruptedException e) { + terminate = true; + // Reset interrupted status + Thread.currentThread().interrupt(); + + } + + while ( (!Thread.currentThread().isInterrupted()) && (!terminate) ) { + + if (message instanceof DataMessage) { + final DataMessage m = (DataMessage) message; + + // Clear is here as the plot should not be cleared after the last point is plotted + // but just before the first point of the next plot (cycle) + if (clearPlot) { + for (Plot plot: plots) { + if(plot instanceof MatrixPlot){ + ((MatrixPlotData) ((MatrixPlot)plot).getData()).clear(); + } + } + clearPlot = false; + } + + for(SeriesDataFilter filter: filters){ + if(filter instanceof XYSeriesDataFilter){ + XYSeriesDataFilter xyfilter = (XYSeriesDataFilter) filter; + + if(xyfilter.getActualSeries()==null || xyfilter.isNewseries()){ + // First series that is filled by this filter! + XYSeriesP s = new XYSeriesP(xyfilter.getSeriesName() + " " + ecount + "-" + xyfilter.getCount()); + ((LinePlot)xyfilter.getPlot()).getData().addSeries(s); + xyfilter.setActualSeries(s); + xyfilter.setNewseries(false); + } + + // XYSeriesP series = ((LinePlot) xyfilter.getPlot()).getData().getSeries(xyfilter.getCount()); // TODO Does not work with multiple series filter per plot !!!! + XYSeriesP series = xyfilter.getActualSeries(); // TODO Does not work with multiple series filter per plot !!!! + // series.add((Double) m.getData().get(xyfilter.getIndexX()), (Double) m.getData().get(xyfilter.getIndexY())); + series.add((Double) m.getData().get(xyfilter.getIndexX()), (Double) m.getData().get(xyfilter.getIndexY()), updateAtStreamElement); + } + if(filter instanceof XYSeriesArrayDataFilter){ + final XYSeriesArrayDataFilter xyfilter = (XYSeriesArrayDataFilter) filter; + + // Ensure that there is no concurrent modification exception or synchronization problems with the + // Swing update task + SwingUtilities.invokeLater(new Runnable(){ + + @Override + public void run() { + XYSeriesP series = new XYSeriesP(xyfilter.getSeriesName() + "-" + xyfilter.getCount()); // Series name must be unique + xyfilter.incrementCount(); + + // ((LinePlot)xyfilter.getPlot()).getData().removeAllSeries(); // Remove all series from the data + // If we can agree only to display one series at a time also a clear() on the actual series is better + XYSeriesCollectionP sc = ((LinePlot)xyfilter.getPlot()).getData(); + sc.addSeries(series); + + // Remove outdated series + if(sc.getSeriesCount()>xyfilter.getMaxSeries()){ + // Remove oldest series + sc.removeSeries(0); + } + + double[] data = (double[]) m.getData().get(xyfilter.getIndexY()); + // Copy data starting from offset to size + int size = data.length; + int offset = xyfilter.getOffset(); + if(xyfilter.getSize()>0 && offset+xyfilter.getSize() vl){ + FilterSet fset = new FilterSet(queue); + List filters = fset.getFilters(); + + for(Visualization v: vl){ + if(v instanceof ch.psi.fda.model.v1.LinePlot){ + ch.psi.fda.model.v1.LinePlot lp = (ch.psi.fda.model.v1.LinePlot) v; + + // Create plot for visualization + ch.psi.plot.xy.LinePlot plot = new ch.psi.plot.xy.LinePlot(lp.getTitle()); + plots.add(plot); + + // Create data filter for visualization + String idX = getId(lp.getX()); + int indexX = queue.getDataMessageMetadata().getIndex(idX); + int dimX = queue.getDataMessageMetadata().getComponents().get(indexX).getDimension(); + + List l = lp.getY(); + for(Object o: l){ + String idY = getId(o); + int indexY = queue.getDataMessageMetadata().getIndex(idY); + int dimY = queue.getDataMessageMetadata().getComponents().get(indexY).getDimension(); + + XYSeriesDataFilter filter = new XYSeriesDataFilter(idX, idY, indexX, indexY, plot); + filter.setDimensionX(dimX); + filter.setDimensionY(dimY); + filter.setSeriesName(idY); + filters.add(filter); + } + } + else if(v instanceof ch.psi.fda.model.v1.LinePlotArray){ + // Array visualization + ch.psi.fda.model.v1.LinePlotArray lp = (ch.psi.fda.model.v1.LinePlotArray) v; + + // Create plot for visualization + ch.psi.plot.xy.LinePlot plot = new ch.psi.plot.xy.LinePlot(lp.getTitle()); + plots.add(plot); + + // Create data filter for visualization + List l = lp.getY(); + for(Object o: l){ + String idY = getId(o); + int indexY = queue.getDataMessageMetadata().getIndex(idY); + int dimY = queue.getDataMessageMetadata().getComponents().get(indexY).getDimension(); + + XYSeriesArrayDataFilter filter = new XYSeriesArrayDataFilter(idY, indexY, plot); + filter.setDimensionY(dimY); + filter.setMaxSeries(lp.getMaxSeries()*lp.getY().size()); // Workaround - keep for each array max series + filter.setOffset(lp.getOffset()); + filter.setSize(lp.getSize()); + filter.setSeriesName(idY); + filters.add(filter); + } + } + else if(v instanceof ch.psi.fda.model.v1.MatrixPlot){ + + // MatrixPlot does currently not support RegionPositioners because of the + // plotting problems this would cause. If regions of the positioner have different + // step sizes it is not easily possible (without (specialized) rasterization) to plot the data. + + ch.psi.fda.model.v1.MatrixPlot mp = (ch.psi.fda.model.v1.MatrixPlot) v; + + + double minX, maxX; + int nX; + double minY, maxY; + int nY; + + String idX, idY, idZ; + + // X Axis + if(mp.getX() instanceof LinearPositioner){ + LinearPositioner linp = ((LinearPositioner)mp.getX()); + idX = linp.getId(); + + minX = (Math.min(linp.getStart(), linp.getEnd())); + maxX = (Math.max(linp.getStart(), linp.getEnd())); + nX = ((int) Math.floor((Math.abs(maxX-minX))/linp.getStepSize()) + 1); + } + else if(mp.getX() instanceof PseudoPositioner){ + PseudoPositioner pp = ((PseudoPositioner)mp.getX()); + idX = pp.getId(); + minX = (1); // Count starts at 1 + maxX = (pp.getCounts()); + nX = (pp.getCounts()); + } + else if(mp.getX() instanceof ContinuousPositioner){ + ContinuousPositioner conp = ((ContinuousPositioner)mp.getX()); + idX = conp.getId(); + + minX = (Math.min(conp.getStart(), conp.getEnd())); + maxX = (Math.max(conp.getStart(), conp.getEnd())); + nX = ((int) Math.floor((Math.abs(maxX-minX))/conp.getStepSize()) + 1); + } + else{ + // Fail as we cannot determine the min, max and number of steps + throw new RuntimeException(mp.getX().getClass().getName()+" is not supported as x-axis of a MatrixPlot"); + } + + // Y Axis + if(mp.getY() instanceof LinearPositioner){ + LinearPositioner linp = ((LinearPositioner)mp.getY()); + idY = linp.getId(); + minY = (Math.min(linp.getStart(), linp.getEnd())); + maxY = (Math.max(linp.getStart(), linp.getEnd())); + nY = ((int) Math.floor((Math.abs(maxY-minY))/linp.getStepSize()) + 1); + } + else if(mp.getY() instanceof PseudoPositioner){ + PseudoPositioner pp = ((PseudoPositioner)mp.getY()); + idY = pp.getId(); + minY = (1); // Count starts at 1 + maxY = (pp.getCounts()); + nY = (pp.getCounts()); + } + else{ + // Fail as we cannot determine the min, max and number of steps + throw new RuntimeException(mp.getY().getClass().getName()+" is not supported as y-axis of a MatrixPlot"); + } + + // Z Dimension + idZ = getId(mp.getZ()); + + // Create plot for visualization + MatrixPlotData data = new MatrixPlotData(minX, maxX, nX, minY, maxY, nY); + MatrixPlot plot = new MatrixPlot(mp.getTitle(), data); + plots.add(plot); + + // Create data filter for visualization + int indexX = queue.getDataMessageMetadata().getIndex(idX); + int indexY = queue.getDataMessageMetadata().getIndex(idY); + int indexZ = queue.getDataMessageMetadata().getIndex(idZ); + + int dimX = queue.getDataMessageMetadata().getComponents().get(indexX).getDimension(); + int dimY = queue.getDataMessageMetadata().getComponents().get(indexY).getDimension(); + int dimZ = queue.getDataMessageMetadata().getComponents().get(indexZ).getDimension(); + + XYZSeriesDataFilter filter = new XYZSeriesDataFilter(idX, idY, idZ, indexX, indexY, indexZ, plot); + filter.setDimensionX(dimX); + filter.setDimensionY(dimY); + filter.setDimensionZ(dimZ); + filters.add(filter); + } + else if(v instanceof ch.psi.fda.model.v1.MatrixPlotArray){ + // Support for 2D waveform plots + ch.psi.fda.model.v1.MatrixPlotArray mp = (ch.psi.fda.model.v1.MatrixPlotArray) v; + + // Get size of the array detector + int arraySize = 0; + Object o = mp.getZ(); + if(o instanceof ArrayDetector){ + ArrayDetector ad = (ArrayDetector) o; + arraySize = ad.getArraySize(); + } + else{ + // Workaround + arraySize = mp.getSize(); // of array is from a manipulation the size is not known. Then the size will indicate the size of the array to display + } + + int offset = mp.getOffset(); + // Determine size for array + int size = mp.getSize(); + if(size>0 && offset+size getPlotPanels(){ + List panels = new ArrayList(); + for(Plot plot: plots){ + panels.add(plot.getChartPanel()); + } + + return panels; + } + + /** + * @param terminateAtEOS the terminateAtEOS to set + */ + public void setTerminateAtEOS(boolean terminateAtEOS) { + this.terminateAtEOS = terminateAtEOS; + } + + /** + * @return the terminateAtEOS + */ + public boolean isTerminateAtEOS() { + return terminateAtEOS; + } + + /** + * @return the updateAtStreamElement + */ + public boolean isUpdateAtStreamElement() { + return updateAtStreamElement; + } + + /** + * @param updateAtStreamElement the updateAtStreamElement to set + */ + public void setUpdateAtStreamElement(boolean updateAtStreamElement) { + this.updateAtStreamElement = updateAtStreamElement; + } + + /** + * @return the updateAtStreamDelimiter + */ + public boolean isUpdateAtStreamDelimiter() { + return updateAtStreamDelimiter; + } + + /** + * @param updateAtStreamDelimiter the updateAtStreamDelimiter to set + */ + public void setUpdateAtStreamDelimiter(boolean updateAtStreamDelimiter) { + this.updateAtStreamDelimiter = updateAtStreamDelimiter; + } + + /** + * @return the updateAtEndOfStream + */ + public boolean isUpdateAtEndOfStream() { + return updateAtEndOfStream; + } + + /** + * @param updateAtEndOfStream the updateAtEndOfStream to set + */ + public void setUpdateAtEndOfStream(boolean updateAtEndOfStream) { + this.updateAtEndOfStream = updateAtEndOfStream; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/XYSeriesArrayDataFilter.java b/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/XYSeriesArrayDataFilter.java new file mode 100644 index 0000000..3cbdc07 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/XYSeriesArrayDataFilter.java @@ -0,0 +1,176 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.visualizer; + +import ch.psi.plot.Plot; + +/** + * Metadata of a group of XYSeries. + * This metadata holds the information needed by the DataVisualizer + * to plot series. + * + * @author ebner + * + */ +public class XYSeriesArrayDataFilter implements SeriesDataFilter { + + private final String idY; + + // Name of the series generated by this filter + private String seriesName = ""; + + // Index of stream message components used for x and y + private final int indexY; + + // Dimension of the x,y value - used to determine whether a new series should be created for this filter + private int dimensionY; + + // Number of series generated for this filter + private int count = 0; + + private int maxSeries = 10; + + // Offset of the array to plot + private int offset = 0; + // Size of the array to plot - size 0 means the complete array + private int size = 0; + + // Plot the data of this filter goes to + private final Plot plot; + + public XYSeriesArrayDataFilter(String idY, int indexY, Plot plot){ + this.idY = idY; + this.indexY = indexY; + this.plot = plot; + } + + /** + * @return the yId + */ + public String getIdY() { + return idY; + } + + /** + * @return the seriesName + */ + public String getSeriesName() { + return seriesName; + } + + /** + * @param seriesName the seriesName to set + */ + public void setSeriesName(String seriesName) { + this.seriesName = seriesName; + } + + /** + * @return the indexY + */ + public int getIndexY() { + return indexY; + } + + /** + * @return the dimensionY + */ + public int getDimensionY() { + return dimensionY; + } + + /** + * @param dimensionY the dimensionY to set + */ + public void setDimensionY(int dimensionY) { + this.dimensionY = dimensionY; + } + + /** + * @return the count + */ + public int getCount() { + return count; + } + + /** + * @param count the count to set + */ + public void setCount(int count) { + this.count = count; + } + + /** + * @return the plot + */ + public Plot getPlot() { + return plot; + } + + /** + * Increment series count + */ + public void incrementCount(){ + count++; + } + + /** + * @param maxSeries the maxSeries to set + */ + public void setMaxSeries(int maxSeries) { + this.maxSeries = maxSeries; + } + + /** + * @return the maxSeries + */ + public int getMaxSeries() { + return maxSeries; + } + + /** + * @return the offset + */ + public int getOffset() { + return offset; + } + + /** + * @param offset the offset to set + */ + public void setOffset(int offset) { + this.offset = offset; + } + + /** + * @return the size + */ + public int getSize() { + return size; + } + + /** + * @param size the size to set + */ + public void setSize(int size) { + this.size = size; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/XYSeriesDataFilter.java b/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/XYSeriesDataFilter.java new file mode 100644 index 0000000..031a29d --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/XYSeriesDataFilter.java @@ -0,0 +1,187 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.visualizer; + +import ch.psi.plot.Plot; +import ch.psi.plot.xy.XYSeriesP; + +/** + * Metadata of a group of XYSeries. + * This metadata holds the information needed by the DataVisualizer + * to plot series. + * + * @author ebner + * + */ +public class XYSeriesDataFilter implements SeriesDataFilter{ + + private final String idX; + private final String idY; + + // Name of the series generated by this filter + private String seriesName = ""; + + // Index of stream message components used for x and y + private final int indexX; + private final int indexY; + + // Dimension of the x,y value - used to determine whether a new series should be created for this filter + private int dimensionX; + private int dimensionY; + + // Number of series generated for this filter + private int count = 0; + + // Plot the data of this filter goes to + private final Plot plot; + + // TODO WORKAROUND - XYZ filter must not have this property !!!! + private XYSeriesP actualSeries = null; + private boolean newseries = false; + + + public XYSeriesDataFilter(String idX, String idY, int indexX, int indexY, Plot plot){ + this.idX = idX; + this.idY = idY; + this.indexX = indexX; + this.indexY = indexY; + this.plot = plot; + } + + /** + * @return the xId + */ + public String getIdX() { + return idX; + } + + /** + * @return the yId + */ + public String getIdY() { + return idY; + } + + /** + * @return the seriesName + */ + public String getSeriesName() { + return seriesName; + } + + /** + * @param seriesName the seriesName to set + */ + public void setSeriesName(String seriesName) { + this.seriesName = seriesName; + } + + /** + * @return the indexX + */ + public int getIndexX() { + return indexX; + } + + /** + * @return the indexY + */ + public int getIndexY() { + return indexY; + } + + /** + * @return the dimensionX + */ + public int getDimensionX() { + return dimensionX; + } + + /** + * @return the dimensionY + */ + public int getDimensionY() { + return dimensionY; + } + + /** + * @param dimensionY the dimensionY to set + */ + public void setDimensionY(int dimensionY) { + this.dimensionY = dimensionY; + } + + /** + * @param dimensionX the dimensionX to set + */ + public void setDimensionX(int dimensionX) { + this.dimensionX = dimensionX; + } + + /** + * @return the count + */ + public int getCount() { + return count; + } + + /** + * @param count the count to set + */ + public void setCount(int count) { + this.count = count; + } + + /** + * @return the plot + */ + public Plot getPlot() { + return plot; + } + + /** + * @return the actualSeries + */ + public XYSeriesP getActualSeries() { + return actualSeries; + } + + /** + * @param actualSeries the actualSeries to set + */ + public void setActualSeries(XYSeriesP actualSeries) { + this.actualSeries = actualSeries; + } + + /** + * @return the newseries + */ + public boolean isNewseries() { + return newseries; + } + + /** + * @param newseries the newseries to set + */ + public void setNewseries(boolean newseries) { + this.newseries = newseries; + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/XYZSeriesArrayDataFilter.java b/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/XYZSeriesArrayDataFilter.java new file mode 100644 index 0000000..f6b5726 --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/XYZSeriesArrayDataFilter.java @@ -0,0 +1,183 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.visualizer; + +import ch.psi.plot.Plot; + +/** + * @author ebner + * + */ +public class XYZSeriesArrayDataFilter implements SeriesDataFilter { + + private final String idY; + private final String idZ; + + // Name of the series generated by this filter + private String seriesName = ""; + + // Index of stream message components used for x and y + private final int indexY; + private final int indexZ; + + // Dimension of the x,y value - used to determine whether a new series should be created for this filter + private int dimensionY; + private int dimensionZ; + + // Number of series generated for this filter + private int count = 0; + + // Plot the data of this filter goes to + private final Plot plot; + + private boolean newseries = false; + + private final int size; + private final int offset; + + public XYZSeriesArrayDataFilter(String idY, String idZ, int indexY, int indexZ, int offset, int size, Plot plot){ + this.idY = idY; + this.idZ = idZ; + this.indexY = indexY; + this.indexZ = indexZ; + this.offset = offset; + this.size = size; + this.plot = plot; + } + + /** + * @return the zId + */ + public String getIdZ() { + return idZ; + } + + /** + * @return the indexZ + */ + public int getIndexZ() { + return indexZ; + } + + /** + * @return the dimensionZ + */ + public int getDimensionZ() { + return dimensionZ; + } + + /** + * @param dimensionZ the dimensionZ to set + */ + public void setDimensionZ(int dimensionZ) { + this.dimensionZ = dimensionZ; + } + + /** + * @return the seriesName + */ + public String getSeriesName() { + return seriesName; + } + + /** + * @param seriesName the seriesName to set + */ + public void setSeriesName(String seriesName) { + this.seriesName = seriesName; + } + + /** + * @return the dimensionX + */ + public int getDimensionY() { + return dimensionY; + } + + /** + * @param dimensionY the dimensionX to set + */ + public void setDimensionY(int dimensionY) { + this.dimensionY = dimensionY; + } + + /** + * @return the count + */ + public int getCount() { + return count; + } + + /** + * @param count the count to set + */ + public void setCount(int count) { + this.count = count; + } + + /** + * @return the newseries + */ + public boolean isNewseries() { + return newseries; + } + + /** + * @param newseries the newseries to set + */ + public void setNewseries(boolean newseries) { + this.newseries = newseries; + } + + /** + * @return the idY + */ + public String getIdY() { + return idY; + } + + /** + * @return the indexX + */ + public int getIndexY() { + return indexY; + } + + /** + * @return the offset + */ + public int getOffset() { + return offset; + } + + /** + * @return the arraySize + */ + public int getSize() { + return size; + } + + /** + * @return the plot + */ + public Plot getPlot() { + return plot; + } +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/XYZSeriesDataFilter.java b/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/XYZSeriesDataFilter.java new file mode 100644 index 0000000..aefcd8d --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/visualizer/XYZSeriesDataFilter.java @@ -0,0 +1,199 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.visualizer; + +import ch.psi.plot.Plot; + +/** + * @author ebner + * + */ +public class XYZSeriesDataFilter implements SeriesDataFilter { + + private final String idX; + private final String idY; + private final String idZ; + + // Name of the series generated by this filter + private String seriesName = ""; + + // Index of stream message components used for x and y + private final int indexX; + private final int indexY; + private final int indexZ; + + // Dimension of the x,y value - used to determine whether a new series should be created for this filter + private int dimensionX; + private int dimensionY; + private int dimensionZ; + + // Number of series generated for this filter + private int count = 0; + + // Plot the data of this filter goes to + private final Plot plot; + + private boolean newseries = false; + + + public XYZSeriesDataFilter(String idX, String idY, String idZ, int indexX, int indexY, int indexZ, Plot plot){ + this.idX = idX; + this.idY = idY; + this.idZ = idZ; + this.indexX = indexX; + this.indexY = indexY; + this.indexZ = indexZ; + this.plot = plot; + } + + /** + * @return the zId + */ + public String getIdZ() { + return idZ; + } + + /** + * @return the indexZ + */ + public int getIndexZ() { + return indexZ; + } + + /** + * @return the dimensionZ + */ + public int getDimensionZ() { + return dimensionZ; + } + + /** + * @param dimensionZ the dimensionZ to set + */ + public void setDimensionZ(int dimensionZ) { + this.dimensionZ = dimensionZ; + } + + /** + * @return the seriesName + */ + public String getSeriesName() { + return seriesName; + } + + /** + * @param seriesName the seriesName to set + */ + public void setSeriesName(String seriesName) { + this.seriesName = seriesName; + } + + /** + * @return the dimensionX + */ + public int getDimensionX() { + return dimensionX; + } + + /** + * @param dimensionX the dimensionX to set + */ + public void setDimensionX(int dimensionX) { + this.dimensionX = dimensionX; + } + + /** + * @return the dimensionY + */ + public int getDimensionY() { + return dimensionY; + } + + /** + * @param dimensionY the dimensionY to set + */ + public void setDimensionY(int dimensionY) { + this.dimensionY = dimensionY; + } + + /** + * @return the count + */ + public int getCount() { + return count; + } + + /** + * @param count the count to set + */ + public void setCount(int count) { + this.count = count; + } + + /** + * @return the newseries + */ + public boolean isNewseries() { + return newseries; + } + + /** + * @param newseries the newseries to set + */ + public void setNewseries(boolean newseries) { + this.newseries = newseries; + } + + /** + * @return the idX + */ + public String getIdX() { + return idX; + } + + /** + * @return the idY + */ + public String getIdY() { + return idY; + } + + /** + * @return the indexX + */ + public int getIndexX() { + return indexX; + } + + /** + * @return the indexY + */ + public int getIndexY() { + return indexY; + } + + /** + * @return the plot + */ + public Plot getPlot() { + return plot; + } + +} diff --git a/ch.psi.fda/src/main/resources/model-v1.xsd b/ch.psi.fda/src/main/resources/model-v1.xsd new file mode 100644 index 0000000..7c0bb42 --- /dev/null +++ b/ch.psi.fda/src/main/resources/model-v1.xsd @@ -0,0 +1,520 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch.psi.fda/src/test/ioc/X/MTEST-PC-X10/App/config/scripts/testmedms.sh b/ch.psi.fda/src/test/ioc/X/MTEST-PC-X10/App/config/scripts/testmedms.sh new file mode 100644 index 0000000..ccbef31 --- /dev/null +++ b/ch.psi.fda/src/test/ioc/X/MTEST-PC-X10/App/config/scripts/testmedms.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +BASEDIR=$(cd $(dirname $0);pwd) + +EPICS_DISPLAY_PATH=/work/sls/config/medm:$EPICS_DISPLAY_PATH +export EPICS_DISPLAY_PATH + +# OTFSCAN MEDM +medm -x -macro "P=MTEST-HW3-OTFX" G_OTFSCAN_user.adl & + +# Motor MEDM +medm -x -macro "P=MTEST-HW3:, M=MOT1" motorx_more.adl & +#medm -x -macro "ENC=MTEST-HW3:EC1" G_ECM_514A_encoder.adl & + +medm -x -macro "P=MTEST-HW3:, M=MOT2" motorx_more.adl & +#medm -x -macro "ENC=MTEST-HW3:EC2" G_ECM_514A_encoder.adl & + +medm -x -macro "P=MTEST-PC-MOTOR:, M=MOT1" motorx_more.adl & + +# Scaler MEDM +medm -x -macro "P=MTEST-HW3:, S=JS" scaler16.adl & \ No newline at end of file diff --git a/ch.psi.fda/src/test/ioc/X/MTEST-PC-X10/MTEST-PC-X10_startup.script b/ch.psi.fda/src/test/ioc/X/MTEST-PC-X10/MTEST-PC-X10_startup.script new file mode 100644 index 0000000..7853d60 --- /dev/null +++ b/ch.psi.fda/src/test/ioc/X/MTEST-PC-X10/MTEST-PC-X10_startup.script @@ -0,0 +1,4 @@ +# Softioc for testing the XEC Java Code + +# Load test template +dbLoadTemplate("MTEST-PC-X10_test.subs") diff --git a/ch.psi.fda/src/test/ioc/X/MTEST-PC-X10/MTEST-PC-X10_test.subs b/ch.psi.fda/src/test/ioc/X/MTEST-PC-X10/MTEST-PC-X10_test.subs new file mode 100644 index 0000000..fac1540 --- /dev/null +++ b/ch.psi.fda/src/test/ioc/X/MTEST-PC-X10/MTEST-PC-X10_test.subs @@ -0,0 +1,5 @@ +file test.template { + { + P = MTEST-PC-X10: + } +} diff --git a/ch.psi.fda/src/test/ioc/X/MTEST-PC-X10/Makefile b/ch.psi.fda/src/test/ioc/X/MTEST-PC-X10/Makefile new file mode 100644 index 0000000..8cccfd6 --- /dev/null +++ b/ch.psi.fda/src/test/ioc/X/MTEST-PC-X10/Makefile @@ -0,0 +1,19 @@ +# $Header: /cvs/X/XASEC/ch.psi.x10/testioc/X/MTEST-PC-X10/Makefile,v 1.1 2010/08/20 11:54:13 ebner Exp $ + +MODULE = MTEST-PC-X10 +export MODULE + +build: + +clean: + +install: build + swit -V -ioc MTEST-PC-X10 + +help: + @echo "The following targets are available with this Makefile:-" + @echo "make (calls default target)" + @echo "make build (default)" + @echo "make install" + @echo "make clean" + @echo "make help" diff --git a/ch.psi.fda/src/test/ioc/X/MTEST-PC-X10/test.template b/ch.psi.fda/src/test/ioc/X/MTEST-PC-X10/test.template new file mode 100644 index 0000000..b7107e6 --- /dev/null +++ b/ch.psi.fda/src/test/ioc/X/MTEST-PC-X10/test.template @@ -0,0 +1,55 @@ +#! Generated by VisualDCT v2.6 +#! DBDSTART +#! DBDEND + + +record(bo, "$(P)BO") { + field(DESC, "Binary Out Record") +} + +# Test analog out record +record(ao, "$(P)AO") { + field(PREC, "6") + field(DESC, "Analog Out Record") +} + +# Double waveform with a precision of 6 +record(waveform, "$(P)DWF") { + field(DESC, "Double Waveform") + field(PREC, "6") + field(NELM, "100") + field(FTVL, "DOUBLE") +} + +# Integer (long) waveform +record(waveform, "$(P)LWF") { + field(DESC, "Long Waveform") + field(NELM, "100") + field(FTVL, "LONG") +} + +# Character waveform of the size 100 +record(waveform, "$(P):CWF") { + field(DESC, "Character Waveform") + field(NELM, "100") + field(FTVL, "CHAR") +} + +# Stringout test record +record(stringout, "$(P)SOUT") { +} + +record(bo, "$(P)BO2") { + field(DESC, "Binary Out Record") + field(VAL, "0") +} + +#! Further lines contain data used by VisualDCT +#! View(0,0,1.0) +#! Record("$(P)BO",20,10,0,1,"$(P)BO") +#! Record("$(P)AO",200,16,0,1,"$(P)AO") +#! Record("$(P)DWF",20,168,0,1,"$(P)DWF") +#! Record("$(P)LWF",220,162,0,1,"$(P)LWF") +#! Record("$(P):CWF",420,162,0,1,"$(P):CWF") +#! Record("$(P)SOUT",380,23,0,1,"$(P)SOUT") +#! Record("$(P)BO2",20,296,0,1,"$(P)BO2") diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/TestChannels.java b/ch.psi.fda/src/test/java/ch/psi/fda/TestChannels.java new file mode 100644 index 0000000..fca5fda --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/TestChannels.java @@ -0,0 +1,35 @@ +/** + * + * Copyright 2011 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This code is distributed in the hope that it will be useful, but without any + * warranty; without even the implied warranty of merchantability or fitness for + * a particular purpose. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ +package ch.psi.fda; + +/** + * @author ebner + * + */ +public class TestChannels { + public static final String BINARY_OUT = "MTEST-PC-X10:BO"; + public static final String BINARY_OUT_TWO = "MTEST-PC-X10:BO2"; + public static final String ANALOG_OUT = "MTEST-PC-X10:AO"; + public static final String STRING_OUT = "MTEST-PC-X10:SOUT"; + public static final String DOUBLE_WAVEFORM = "MTEST-PC-X10:DWF"; + public static final String STRING_OUT_NOT_EXIST = "MTEST-PC-X10:SOUT-NOT-EXIST"; + + public static final String MOTOR_ONE = "MTEST-HW3:MOT1"; + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/aq/AcquisitionEngineConfigurationTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/aq/AcquisitionEngineConfigurationTest.java new file mode 100644 index 0000000..f23b61c --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/aq/AcquisitionEngineConfigurationTest.java @@ -0,0 +1,75 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.aq; + +import java.io.File; +import java.net.URI; +import java.net.URL; +import java.util.logging.Logger; + +import junit.framework.Assert; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.aq.AcquisitionEngineConfiguration; + +/** + * @author ebner + * + */ +public class AcquisitionEngineConfigurationTest { + + // Get Logger + private static Logger logger = Logger.getLogger(AcquisitionEngineConfigurationTest.class.getName()); + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + URL url = this.getClass().getClassLoader().getResource("home/config/fda.properties"); + System.setProperty("ch.psi.fda.config.file", new File(new URI(url.toString())).getAbsolutePath()); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + + /** + * Test method for {@link ch.psi.fda.aq.AcquisitionEngineConfiguration#getDataFilePrefix()}. + * Test whether the supported date macros in the file prefix are resolved + */ + @Test + public void testGetConfiguration() { + AcquisitionEngineConfiguration configuration = AcquisitionEngineConfiguration.getInstance(); + String s = configuration.getDataFilePrefix(); + if(s==null){ + Assert.fail("No configuration returned for data file prefix"); + } + logger.info("Data file prefix: "+s); + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/actions/ChannelAccessConditionTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/actions/ChannelAccessConditionTest.java new file mode 100644 index 0000000..07bd648 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/actions/ChannelAccessConditionTest.java @@ -0,0 +1,178 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actions; + +import static org.junit.Assert.fail; + +import java.util.logging.Logger; + +import gov.aps.jca.CAException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.TestChannels; +import ch.psi.fda.core.actions.ChannelAccessCondition; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * @author ebner + * + */ +public class ChannelAccessConditionTest { + + // Get Logger + private static Logger logger = Logger.getLogger(ChannelAccessConditionTest.class.getName()); + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringCondition#ChannelAccessStringCondition(java.lang.String, java.lang.String, long)}. + */ + @Test + public void testChannelAccessStringCondition() { + new ChannelAccessCondition(TestChannels.STRING_OUT, "SomeValue", 1000l); + } + + /** + * Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringCondition#ChannelAccessStringCondition(java.lang.String, java.lang.String, long)}. + */ + @Test(expected=IllegalArgumentException.class) + public void testChannelAccessStringConditionNotExist() { + new ChannelAccessCondition(TestChannels.STRING_OUT_NOT_EXIST, "SomeValue", 1000l); + } + + /** + * Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringCondition#execute()}. + * @throws CAException + * @throws InterruptedException + */ + @Test + public void testExecute() throws CAException, InterruptedException { + String value = "SomeValue"; + + final ChannelAccessCondition action = new ChannelAccessCondition(TestChannels.STRING_OUT, value, 10000l); + final ChannelBean channel = ChannelBeanFactory.getFactory().createChannelBean(String.class, TestChannels.STRING_OUT, false); + + Thread t = new Thread(new Runnable() { + + @Override + public void run() { + try { + action.execute(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + }); + + // Set the value to something else than the expected value + channel.setValue(value+"hello"); + + // Start new thread that is executing the actions execute() method + logger.fine("Execute action"); + t.start(); + + // Wait some time and set the channel to the expected value + Thread.sleep(1000); + logger.fine("Set channel value to "+value); + channel.setValue(value); + + // Wait thread to join and check whether the thread has terminated + t.join(100); + if(t.isAlive()){ + fail("Action did not return although the value was reached"); + } + } + + @Test(expected=RuntimeException.class) + public void testExecuteFail() throws CAException, InterruptedException { + String value = "SomeValue"; + + final ChannelAccessCondition action = new ChannelAccessCondition(TestChannels.STRING_OUT, value, 9000l); + final ChannelBean channel = ChannelBeanFactory.getFactory().createChannelBean(String.class, TestChannels.STRING_OUT, false); + + + // Set the value to something else than the expected value + channel.setValue(value+"hello"); + action.execute(); + } + + + /** + * Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringCondition#execute()}. + * @throws CAException + * @throws InterruptedException + */ + @Test + public void testExecuteInteger() throws CAException, InterruptedException { + Integer value = 12; + + final ChannelAccessCondition action = new ChannelAccessCondition(TestChannels.STRING_OUT, value, 10000l); + final ChannelBean channel = ChannelBeanFactory.getFactory().createChannelBean(Integer.class, TestChannels.STRING_OUT, false); + + Thread t = new Thread(new Runnable() { + + @Override + public void run() { + try { + action.execute(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + }); + + // Set the value to something else than the expected value + channel.setValue(value+1); + + // Start new thread that is executing the actions execute() method + logger.fine("Execute action"); + t.start(); + + // Wait some time and set the channel to the expected value + Thread.sleep(1000); + logger.fine("Set channel value to "+value); + channel.setValue(value); + + // Wait thread to join and check whether the thread has terminated + t.join(100); + if(t.isAlive()){ + fail("Action did not return although the value was reached"); + } + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/actions/ChannelAccessPutTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/actions/ChannelAccessPutTest.java new file mode 100644 index 0000000..45cddd1 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/actions/ChannelAccessPutTest.java @@ -0,0 +1,159 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actions; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.List; + +import gov.aps.jca.CAException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.TestChannels; +import ch.psi.fda.core.actions.ChannelAccessPut; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * Test class for ChannelAccessStringPut + * @author ebner + * + */ +public class ChannelAccessPutTest { + + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringPut#ChannelAccessStringPut(java.lang.String, java.lang.String, boolean)}. + * Test correct initialization + */ + @Test + public void testChannelAccessStringPutStringStringBoolean() { + new ChannelAccessPut(TestChannels.STRING_OUT, "SomeValue", true, null); + } + + /** + * Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringPut#ChannelAccessStringPut(java.lang.String, java.lang.String, boolean)}. + * Test whether the correct Exception is thrown if a non existing channel is specified at creation time + */ + @Test(expected=IllegalArgumentException.class) + public void testChannelAccessStringPutStringStringBooleanNotExist() { + new ChannelAccessPut(TestChannels.STRING_OUT_NOT_EXIST, "SomeValue", true, null); + } + + /** + * Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringPut#ChannelAccessStringPut(java.lang.String, java.lang.String)}. + * Test correct initialization + */ + @Test + public void testChannelAccessStringPutStringString() { + new ChannelAccessPut(TestChannels.STRING_OUT, "SomeValue"); + } + + /** + * Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringPut#ChannelAccessStringPut(java.lang.String, java.lang.String)}. + * Test whether the correct Exception is thrown if a non existing channel is specified at creation time + */ + @Test(expected=IllegalArgumentException.class) + public void testChannelAccessStringPutStringStringNotExist() { + new ChannelAccessPut(TestChannels.STRING_OUT_NOT_EXIST, "SomeValue"); + } + + /** + * Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringPut#execute()}. + * @throws CAException + * @throws InterruptedException + */ + @Test + public void testExecute() throws CAException, InterruptedException { + + String setValue = "MyTestString"; + ChannelAccessPut action = new ChannelAccessPut(TestChannels.STRING_OUT, setValue); + action.execute(); + + ChannelBean channel = ChannelBeanFactory.getFactory().createChannelBean(String.class, TestChannels.STRING_OUT, false); + String value = channel.getValue(); + + if(!value.equals(setValue)){ + fail("Test string was not set correctly"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringPut#execute()}. + * @throws CAException + */ + @Test + public void testExecuteString() throws CAException, InterruptedException { + + String setValue = "MyTestString"; + + ChannelBean channel = ChannelBeanFactory.getFactory().createChannelBean(String.class, TestChannels.STRING_OUT, false); + // Set channel to something else + channel.setValue(setValue+"test"); + + ChannelAccessPut action = new ChannelAccessPut(TestChannels.STRING_OUT, setValue); + action.execute(); + + + String value = channel.getValue(); + + if(!value.equals(setValue)){ + fail("Test string was not set correctly"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringPut#execute()}. + * @throws CAException + * @throws InterruptedException + */ + @Test + public void testExecuteMulti() throws CAException, InterruptedException { + + List> actions = new ArrayList>(); + for(int i=0;i<20;i++){ + actions.add(new ChannelAccessPut(TestChannels.STRING_OUT, i+"")); + } + + for(ChannelAccessPut action: actions){ + action.execute(); + } + + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/actions/DelayTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/actions/DelayTest.java new file mode 100644 index 0000000..9702357 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/actions/DelayTest.java @@ -0,0 +1,104 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actions; + +import static org.junit.Assert.*; + +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.core.actions.Delay; + +/** + * @author ebner + * + */ +public class DelayTest { + + // Get Logger + private static Logger logger = Logger.getLogger(DelayTest.class.getName()); + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.actions.Delay#Delay(long)}. + * Test correct initialization + */ + @Test + public void testDelay() { + new Delay(1000); + new Delay(1); + } + + /** + * Test method for {@link ch.psi.fda.core.actions.Delay#Delay(long)}. + * Test initialization with time = 0 + */ + @Test(expected=IllegalArgumentException.class) + public void testDelayZeroWait() { + new Delay(0); + } + + /** + * Test method for {@link ch.psi.fda.core.actions.Delay#Delay(long)}. + * Test initialization with time < 0 + */ + @Test(expected=IllegalArgumentException.class) + public void testDelayNegativeWait() { + new Delay(-10000); + } + + /** + * Test method for {@link ch.psi.fda.core.actions.Delay#execute()}. + * @throws InterruptedException + */ + @Test + public void testExecute() throws InterruptedException { + long wait = 1000; + Delay action = new Delay(wait); + long start = System.currentTimeMillis(); + action.execute(); + long end = System.currentTimeMillis(); + + // Check whether wait was of the correct length + // Accept 10% "jitter" because of the delays of the function calls, etc. + long elapsed = end-start; + logger.fine("Elapsed wait time: "+elapsed+" - Specified wait time: "+wait); + if(elapsed>(wait*1.1)){ + fail("Wait was not of the correct length"); + } + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/actions/JythonActionTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/actions/JythonActionTest.java new file mode 100644 index 0000000..0e01589 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/actions/JythonActionTest.java @@ -0,0 +1,103 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actions; + +import gov.aps.jca.CAException; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.TestChannels; +import ch.psi.fda.core.actions.JythonAction; +import ch.psi.fda.core.scripting.JythonParameterMappingChannel; + +/** + * @author ebner + * + */ +public class JythonActionTest { + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.actions.JythonAction#execute()}. + * @throws CAException + */ + @Test + public void testExecute() throws CAException { + String script = "import math\ndef process(o):\n print o.getValue()\n o.setValue(-1.0)\n print o.getValue()\n val=math.cos(10.0) + math.sin(o.getValue())\n print val\n o.setValue(val)"; + List mapping = new ArrayList(); + mapping.add(new JythonParameterMappingChannel("o", TestChannels.ANALOG_OUT, Double.class)); + JythonAction action = new JythonAction(script, mapping); + + // Execute action + action.execute(); + } + + /** + * Check whether too much mappings are detected + * Test method for {@link ch.psi.fda.core.actions.JythonAction#execute()}. + * @throws CAException + */ + @Test(expected=IllegalArgumentException.class) + public void testWrongMapping1() throws CAException { + String script = "import math\ndef process(o):\n print o.getValue()\n o.setValue(-1.0)\n print o.getValue()\n val=math.cos(10.0) + math.sin(o.getValue())\n print val\n o.setValue(val)"; + List mapping = new ArrayList(); + mapping.add(new JythonParameterMappingChannel("o", TestChannels.ANALOG_OUT, Double.class)); + mapping.add(new JythonParameterMappingChannel("b", TestChannels.ANALOG_OUT, Double.class)); + JythonAction action = new JythonAction(script, mapping); + + // Execute action + action.execute(); + } + + /** + * Check whether a mapping to a wrong variable is detected + * Test method for {@link ch.psi.fda.core.actions.JythonAction#execute()}. + * @throws CAException + */ + @Test(expected=IllegalArgumentException.class) + public void testWrongMapping2() throws CAException { + String script = "import math\ndef process(o):\n print o.getValue()\n o.setValue(-1.0)\n print o.getValue()\n val=math.cos(10.0) + math.sin(o.getValue())\n print val\n o.setValue(val)"; + List mapping = new ArrayList(); + mapping.add(new JythonParameterMappingChannel("ooo", TestChannels.ANALOG_OUT, Double.class)); + JythonAction action = new JythonAction(script, mapping); + + // Execute action + action.execute(); + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/actions/ShellActionTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/actions/ShellActionTest.java new file mode 100644 index 0000000..e6c79a2 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/actions/ShellActionTest.java @@ -0,0 +1,119 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actions; + +import java.io.File; +import java.net.URI; +import java.net.URL; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.core.actions.ShellAction; + +/** + * @author ebner + * + */ +public class ShellActionTest { + + /** + * Test Shell Script that returns with an exit code of 0 + */ + private String testscript; + /** + * Test Shell Script that returns with an exit code of 1 + */ + private String testscriptError; + /** + * Test Shell Script that does not exist + */ + private String testscriptNotExist; + + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + URL url = this.getClass().getClassLoader().getResource("testscripts"); + String file = new File(new URI(url.toString())).getAbsolutePath(); + testscript = file+"/testscript1.sh"; + testscriptError = file+"testfiles/testscript2-error.sh"; + testscriptNotExist = file+"testfiles/testscriptNotExist.sh"; + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.actions.ShellAction#ScriptAction(java.lang.String)}. + * Test the creation of a ScriptAction object with an existing script. + */ + @Test + public void testScriptAction() { + new ShellAction(testscript); + } + + /** + * Test method for {@link ch.psi.fda.core.actions.ShellAction#ScriptAction(java.lang.String)}. + * Test the creation of a ScriptAction object with a non existing script. IllegalArgumentException expected ... + */ + @Test(expected=IllegalArgumentException.class) + public void testScriptActionScriptNotExit() { + new ShellAction(testscriptNotExist); + } + + /** + * Test method for {@link ch.psi.fda.core.actions.ShellAction#execute()}. + * @throws InterruptedException + */ + @Test + public void testExecute() throws InterruptedException { + File f = new File(testscript); + f.setExecutable(true); // Make file executable + ShellAction action = new ShellAction(testscript); + action.execute(); + } + + /** + * Test method for {@link ch.psi.fda.core.actions.ShellAction#execute()}. + * @throws InterruptedException + */ + @Test(expected=RuntimeException.class) + public void testExecuteError() throws InterruptedException { + ShellAction action = new ShellAction(testscriptError); + action.execute(); + } + + /** + * Test method for {@link ch.psi.fda.core.actions.ShellAction#abort()}. + */ + @Test + public void testAbort() { + // Abort function in ScriptAction is not implemented - therefore there is nothing to test. + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/ChannelAccessFunctionActuatorTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/ChannelAccessFunctionActuatorTest.java new file mode 100644 index 0000000..4496bd2 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/ChannelAccessFunctionActuatorTest.java @@ -0,0 +1,759 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actors; + + +import static org.junit.Assert.fail; +import gov.aps.jca.CAException; + +import java.net.SocketException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.TestChannels; +import ch.psi.fda.core.actors.ChannelAccessFunctionActuator; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * Test class specific for the FunctionActuatorChannelAccess implementation. + * @author ebner + * + */ +public class ChannelAccessFunctionActuatorTest { + + // Get Logger + private static Logger logger = Logger.getLogger(ChannelAccessFunctionActuatorTest.class.getName()); + + private static final String channelName = TestChannels.ANALOG_OUT; + private static final String channelNameDone = TestChannels.BINARY_OUT; + private static final int doneValue = 1; + private static final double doneDelay = 0; + private static final Long timeout = 1800000l; // 30 minutes + + private ChannelBean channel; + private ChannelBean doneChannel; + + private Function function; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + channel = ChannelBeanFactory.getFactory().createChannelBean(Double.class, channelName, false); + doneChannel = ChannelBeanFactory.getFactory().createChannelBean(Integer.class, channelNameDone, false); + + function = new Function() { + + @Override + public double calculate(double parameter) { + return parameter+2; // Translation by 2 + } + }; + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#FunctionActuatorChannelAccess(String, double, double, double)}. + * Check whether Exception is thrown if a negative step size is specified. + */ + @Test(expected=IllegalArgumentException.class) + public void testChannelAccessFunctionActuatorNegativeStepSize() throws SocketException, CAException, Exception { + // Need to throw exception because of negative step size + new ChannelAccessFunctionActuator(channelName, function,0, 1, -0.1, timeout); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#FunctionActuatorChannelAccess(String, double, double, double)}. + */ + @Test + public void testChannelAccessFunctionActuatorTimeout() throws SocketException, CAException, Exception { + + // Negative timeout + boolean flag = false; + try{ + // Need to return IllegalArgumentException due to negative Timeout + new ChannelAccessFunctionActuator(channelName, function, 0, 1, 0.1, -1l); + } + catch(IllegalArgumentException e){ + flag=true; + } + if(!flag){ + fail("Negative timeout is not handeled correctly"); + } + + // 0 timeout + flag=false; + try{ + // Need to return IllegalArgumentException + new ChannelAccessFunctionActuator(channelName, function, 0, 1, 0.1, -0l); + } + catch(IllegalArgumentException e){ + flag=true; + } + if(!flag){ + fail("0 timeout is not handled correctly"); + } + + // Accept null timeout + new ChannelAccessFunctionActuator(channelName, function, 0, 1, 0.1, null); + + // Accept positive timeout + new ChannelAccessFunctionActuator(channelName, function, 0, 1, 0.1, 1l); + + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#FunctionActuatorChannelAccess(String, double, double, double)}. + * Check whether Exception is thrown if a zero step size is specified. + */ + @Test(expected=IllegalArgumentException.class) + public void testChannelAccessFunctionActuatorZeroStepSize() throws SocketException, CAException, Exception { + // Zero step size need to cause an exception + new ChannelAccessFunctionActuator(channelName, function, 0, 1, 0, timeout); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#FunctionActuatorChannelAccess(String, double, double, double)}. + * Check correct initialization + */ + public void testChannelAccessFunctionActuator() throws SocketException, CAException, Exception { + // Zero step size need to cause an exception + new ChannelAccessFunctionActuator(channelName, function, 0, 10, 1, timeout); + } + + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#set()}. + * @throws InterruptedException + */ + @Test + public void testSet() throws InterruptedException { + ChannelAccessFunctionActuator actuator = new ChannelAccessFunctionActuator(channelName, function, 0, 0.09999, 0.1, timeout); + actuator.set(); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#hasNext()}. + * Check whether the actuator throws an Exception if there is no next point but set() is called + * @throws InterruptedException + */ + @Test(expected=IllegalStateException.class) + public void testSetNoNext() throws InterruptedException { + ChannelAccessFunctionActuator actuator = new ChannelAccessFunctionActuator(channelName, function, 0, 0.09999, 0.1, timeout); + actuator.set(); + actuator.set(); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#hasNext()}. + * @throws InterruptedException + */ + @Test + public void testHasNextOneStep() throws InterruptedException { + ChannelAccessFunctionActuator actuator = new ChannelAccessFunctionActuator(channelName, function, 0, 0.09999, 0.1, timeout); + // Execute first set (because there is always a first move) + actuator.set(); + + // Check whether actuator returns that there is no next point + boolean next = actuator.hasNext(); + if(next){ + fail("There must be no next step"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#hasNext()}. + * @throws InterruptedException + */ + @Test + public void testHasNext() throws InterruptedException { + ChannelAccessFunctionActuator actuator = new ChannelAccessFunctionActuator(channelName, function, 0, 10, 0.1, timeout); + + int count = 0; + int steps = (int) ((10-0)/0.1)+1; + while(actuator.hasNext()){ + actuator.set(); + count++; + } + + logger.fine("Actual steps: "+count+" - Needed steps: "+steps); + if(count != steps){ + fail("Actuator set more steps than specified"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#set()}. + * Test actuator move startend ... + * @throws InterruptedException + */ + @Test + public void testSetLoop() throws InterruptedException { + + List settings = new ArrayList(); + // start end stepsize + settings.add(new double[] {0, 10, 0.1}); + settings.add(new double[] {0, -1, 0.1}); + + + for(double[] svalue: settings){ + ChannelAccessFunctionActuator actuator = new ChannelAccessFunctionActuator(channelName, function, svalue[0], svalue[1], svalue[2], timeout); + + int count =0; + while(actuator.hasNext()){ + actuator.set(); + count++; + } + + int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1; + + if(count != cnt){ + fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]"); + } + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#set()}. + * Test actuator move startend ... + * @throws CAException + */ + @Test + public void testReverse() throws CAException, InterruptedException { + + List settings = new ArrayList(); + // start end stepsize + settings.add(new double[] {0, 10, 0.1}); + settings.add(new double[] {0, -1, 0.1}); + + + for(double[] svalue: settings){ + ChannelAccessFunctionActuator actuator = new ChannelAccessFunctionActuator(channelName, function, svalue[0], svalue[1], svalue[2], timeout); + actuator.init(); + + int count =0; + while(actuator.hasNext()){ + actuator.set(); + + // Check set value + double val = channel.getValue(); + logger.info("Value: "+val); + if(svalue[0] 0.001){ // 0.001 is precision + fail("Set value ["+sval+"] does not match actual value ["+val+"]"); + } + } + else{ + double sval = function.calculate(svalue[0]-(count*svalue[2])); + if(Math.abs(val-sval) > 0.001){ // 0.001 is precision + fail("Set value ["+sval+"] does not match actual value ["+val+"]"); + } + } + count++; + } + + int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1; + + if(count != cnt){ + fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]"); + } + + + actuator.reverse(); + actuator.init(); + count =0; + while(actuator.hasNext()){ + actuator.set(); + + // Check set value + double val = channel.getValue(); + logger.info("Value: "+val); + if(svalue[1] 0.001){ // 0.001 is precision + fail("Set value ["+sval+"] does not match actual value ["+val+"]"); + } + } + else{ + double sval = function.calculate(svalue[1]-(count*svalue[2])); + if(Math.abs(val-sval) > 0.001){ // 0.001 is precision + fail("Set value ["+sval+"] does not match actual value ["+val+"]"); + } + } + + count++; + } + + cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1; + + if(count != cnt){ + fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]"); + } + + + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#set()}. + * Test actuator move startend ... + * @throws CAException + */ + @Test + public void testReverseReset() throws CAException, InterruptedException { + + List settings = new ArrayList(); + // start end stepsize + settings.add(new double[] {0, 10, 0.1}); + settings.add(new double[] {0, -1, 0.1}); + + + for(double[] svalue: settings){ + ChannelAccessFunctionActuator actuator = new ChannelAccessFunctionActuator(channelName, function, svalue[0], svalue[1], svalue[2], timeout); + actuator.init(); + + int count =0; + while(actuator.hasNext()){ + actuator.set(); + + // Check set value + double val = channel.getValue(); + logger.info("Value: "+val); + if(svalue[0] 0.001){ // 0.001 is precision + fail("Set value ["+sval+"] does not match actual value ["+val+"]"); + } + } + else{ + double sval = function.calculate(svalue[0]-(count*svalue[2])); + if(Math.abs(val-sval) > 0.001){ // 0.001 is precision + fail("Set value ["+sval+"] does not match actual value ["+val+"]"); + } + } + count++; + } + + int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1; + + if(count != cnt){ + fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]"); + } + + + actuator.reverse(); + actuator.reset(); + actuator.init(); + count =0; + while(actuator.hasNext()){ + actuator.set(); + + // Check set value + double val = channel.getValue(); + logger.info("Value: "+val); + if(svalue[0] 0.001){ // 0.001 is precision + fail("Set value ["+sval+"] does not match actual value ["+val+"]"); + } + } + else{ + double sval = function.calculate(svalue[0]-(count*svalue[2])); + if(Math.abs(val-sval) > 0.001){ // 0.001 is precision + fail("Set value ["+sval+"] does not match actual value ["+val+"]"); + } + } + count++; + } + + cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1; + + if(count != cnt){ + fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]"); + } + + + } + } + + + // TODO Check if actuator makes the required number of steps + // TODO Check if actuator makes the steps in the right direction + // TODO Check if actuator really blocks until motor/actuator is moved. + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#FunctionActuatorChannelAccess(String, double, double, double)}. + * Check whether Exception is thrown if a negative step size is specified. + */ + @Test(expected=IllegalArgumentException.class) + public void testChannelAccessGPFunctionActuatorNegativeStepSize() throws SocketException, CAException, Exception { + // Need to throw exception because of negative step size + new ChannelAccessFunctionActuator(channelName, channelNameDone, doneValue, doneDelay, function, 0, 1, -0.1, timeout); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#FunctionActuatorChannelAccess(String, double, double, double)}. + * Check whether Exception is thrown if a zero step size is specified. + */ + @Test(expected=IllegalArgumentException.class) + public void testChannelAccessGPFunctionActuatorZeroStepSize() throws SocketException, CAException, Exception { + // Zero step size need to cause an exception + new ChannelAccessFunctionActuator(channelName, channelNameDone, doneValue, doneDelay, function, 0, 1, 0, timeout); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#FunctionActuatorChannelAccess(String, double, double, double)}. + * Check correct initialization + */ + public void testChannelAccessGPFunctionActuator() throws SocketException, CAException, Exception { + // Zero step size need to cause an exception + new ChannelAccessFunctionActuator(channelName, channelNameDone, doneValue, doneDelay, function, 0, 10, 1, timeout); + } + + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#set()}. + * @throws CAException + */ + @Test + public void testDoneSet() throws CAException, InterruptedException { + + ChannelAccessFunctionActuator actuator = new ChannelAccessFunctionActuator(channelName, channelNameDone, doneValue, doneDelay, function, 0, 0.09999, 0.1, timeout); + + // Simulate done channel + doneChannel.setValue(0); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(3000); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + + actuator.set(); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#set()}. + * @throws CAException + */ + @Test + public void testDoneDelay() throws CAException, InterruptedException { + + ChannelAccessFunctionActuator actuator = new ChannelAccessFunctionActuator(channelName, channelNameDone, doneValue, 1.5, function, 0, 1, 0.1, timeout); + + // Simulate done channel + doneChannel.setValue(1); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(1000); + doneChannel.setValue(0); + Thread.sleep(4000); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + + long start = System.currentTimeMillis(); + actuator.set(); + long end = System.currentTimeMillis(); + + logger.fine("Elapsed time: "+(end-start)); + if((end-start)<4000){ // Check whether all the moves took less than 6 seconds (thats the delay the done is set to 1) + fail("Done delay does not work"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#hasNext()}. + * Check whether the actuator throws an Exception if there is no next point but set() is called + * @throws CAException + */ + @Test(expected=IllegalStateException.class) + public void testDoneSetNoNext() throws CAException, InterruptedException { + + ChannelAccessFunctionActuator actuator = new ChannelAccessFunctionActuator(channelName, channelNameDone, doneValue, doneDelay, function,0, 0.09999, 0.1, timeout); + + + // Simulate done channel + doneChannel.setValue(0); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(3000); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + + actuator.set(); + + + // Simulate done channel + doneChannel.setValue(0); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(3000); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + + actuator.set(); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#hasNext()}. + * @throws CAException + */ + @Test + public void testDoneHasNextOneStep() throws CAException, InterruptedException { + ChannelAccessFunctionActuator actuator = new ChannelAccessFunctionActuator(channelName, channelNameDone, doneValue, doneDelay, function, 0, 0.09999, 0.1, timeout); + + // Simulate done channel + doneChannel.setValue(0); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(3000); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + + // Execute first set (because there is always a first move) + actuator.set(); + + // Check whether actuator returns that there is no next point + boolean next = actuator.hasNext(); + if(next){ + fail("There must be no next step"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#hasNext()}. + * @throws CAException + */ + @Test + public void testDoneHasNext() throws CAException, InterruptedException { + ChannelAccessFunctionActuator actuator = new ChannelAccessFunctionActuator(channelName, channelNameDone, doneValue, doneDelay, function,0, 10, 0.1, timeout); + + int count = 0; + int steps = (int) ((10-0)/0.1)+1; + while(actuator.hasNext()){ + + // Simulate done channel + doneChannel.setValue(0); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(10); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + + actuator.set(); + count++; + } + + logger.fine("Actual steps: "+count+" - Needed steps: "+steps); + if(count != steps){ + fail("Actuator set more steps than specified"); + } + } + + + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#set()}. + * Test actuator move startend ... + * @throws CAException + */ + @Test + public void testDoneSetLoop() throws CAException, InterruptedException { + + List settings = new ArrayList(); + // start end stepsize + settings.add(new double[] {0, 10, 0.1}); + settings.add(new double[] {0, -1, 0.1}); + + + for(double[] svalue: settings){ + ChannelAccessFunctionActuator actuator = new ChannelAccessFunctionActuator(channelName, channelNameDone, doneValue, doneDelay, function, svalue[0], svalue[1], svalue[2], timeout); + + int count =0; + while(actuator.hasNext()){ + + // Simulate done channel + doneChannel.setValue(0); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(10); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + + actuator.set(); + count++; + } + + int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1; + + if(count != cnt){ + fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]"); + } + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#set()}. + * Test whether the actuator returns if the actuator is already on the position it should be before the move ... + * (see issue XASEC-278) + * @throws CAException + */ + @Test + public void testMoveToActualPosition() throws CAException, InterruptedException { + + double start = 0; + double end = 2; + double stepSize = 1; + + channel.setValue(start); + + Thread.sleep(1000); + + logger.info("Current channel value: "+channel.getValue()); + + ChannelAccessFunctionActuator actuator = new ChannelAccessFunctionActuator(channelName, channelNameDone, doneValue, doneDelay, function, start, end, stepSize, timeout); + while(actuator.hasNext()){ + + // Simulate done channel + doneChannel.setValue(1); +// new Thread(new Runnable() { +// +// @Override +// public void run() { +// +// try { +// Thread.sleep(10); +//// doneChannel.setValue(1); +// } catch (Exception e) { +// } +// +// } +// }).start(); + + actuator.set(); + } + + } + + + @Test + public void testSetLoop2() throws InterruptedException { + + Function function = new Function() { + + @Override + public double calculate(double parameter) { + return parameter+2; + } + }; + + List settings = new ArrayList(); + // start end stepsize + settings.add(new double[] {0, 10, 0.1}); + settings.add(new double[] {0, -1, 0.1}); + + + for(double[] svalue: settings){ + ChannelAccessFunctionActuator actuator = new ChannelAccessFunctionActuator(channelName, function, svalue[0], svalue[1], svalue[2], timeout); + + int count =0; + while(actuator.hasNext()){ + actuator.set(); + count++; + } + + int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1; + + if(count != cnt){ + fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]"); + } + } + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/ChannelAccessLinearActuatorTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/ChannelAccessLinearActuatorTest.java new file mode 100644 index 0000000..4fcb72c --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/ChannelAccessLinearActuatorTest.java @@ -0,0 +1,709 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actors; + + +import static org.junit.Assert.fail; +import gov.aps.jca.CAException; + +import java.net.SocketException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.TestChannels; +import ch.psi.fda.core.actors.ChannelAccessLinearActuator; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * Test class specific for the LinearActuatorChannelAccess implementation. + * @author ebner + * + */ +public class ChannelAccessLinearActuatorTest { + + // Get Logger + private static Logger logger = Logger.getLogger(ChannelAccessLinearActuatorTest.class.getName()); + + private static final String channelName = TestChannels.ANALOG_OUT; + private static final String channelNameDone = TestChannels.BINARY_OUT; + private static final int doneValue = 1; + private static final double doneDelay = 0; + private static final Long timeout = 1800000l; // 30 minutes + + private ChannelBean channel; + private ChannelBean doneChannel; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + channel = ChannelBeanFactory.getFactory().createChannelBean(Double.class, channelName, false); + doneChannel = ChannelBeanFactory.getFactory().createChannelBean(Integer.class, channelNameDone, false); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#LinearActuatorChannelAccess(String, double, double, double)}. + * Check whether Exception is thrown if a negative step size is specified. + */ + @Test(expected=IllegalArgumentException.class) + public void testChannelAccessLinearActuatorNegativeStepSize() throws SocketException, CAException, Exception { + // Need to throw exception because of negative step size + new ChannelAccessLinearActuator(channelName, null, 1,0, 0, 1, -0.1, timeout); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#LinearActuatorChannelAccess(String, double, double, double)}. + */ + @Test + public void testChannelAccessLinearActuatorTimeout() throws SocketException, CAException, Exception { + + // Negative timeout + boolean flag = false; + try{ + // Need to return IllegalArgumentException due to negative Timeout + new ChannelAccessLinearActuator(channelName, null, 1,0, 0, 1, 0.1, -1l); + } + catch(IllegalArgumentException e){ + flag=true; + } + if(!flag){ + fail("Negative timeout is not handeled correctly"); + } + + // 0 timeout + flag=false; + try{ + // Need to return IllegalArgumentException + new ChannelAccessLinearActuator(channelName, null, 1,0, 0, 1, 0.1, -0l); + } + catch(IllegalArgumentException e){ + flag=true; + } + if(!flag){ + fail("0 timeout is not handled correctly"); + } + + // Accept null timeout + new ChannelAccessLinearActuator(channelName, null, 1,0, 0, 1, 0.1, null); + + // Accept positive timeout + new ChannelAccessLinearActuator(channelName, null, 1,0, 0, 1, 0.1, 1l); + + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#LinearActuatorChannelAccess(String, double, double, double)}. + * Check whether Exception is thrown if a zero step size is specified. + */ + @Test(expected=IllegalArgumentException.class) + public void testChannelAccessLinearActuatorZeroStepSize() throws SocketException, CAException, Exception { + // Zero step size need to cause an exception + new ChannelAccessLinearActuator(channelName, null, 1,0, 0, 1, 0, timeout); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#LinearActuatorChannelAccess(String, double, double, double)}. + * Check correct initialization + */ + public void testChannelAccessLinearActuator() throws SocketException, CAException, Exception { + // Zero step size need to cause an exception + new ChannelAccessLinearActuator(channelName, null, 1,0, 0, 10, 1, timeout); + } + + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#set()}. + * @throws InterruptedException + */ + @Test + public void testSet() throws InterruptedException { + ChannelAccessLinearActuator actuator = new ChannelAccessLinearActuator(channelName, null, 1,0, 0, 0.09999, 0.1, timeout); + actuator.set(); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#hasNext()}. + * Check whether the actuator throws an Exception if there is no next point but set() is called + * @throws InterruptedException + */ + @Test(expected=IllegalStateException.class) + public void testSetNoNext() throws InterruptedException { + ChannelAccessLinearActuator actuator = new ChannelAccessLinearActuator(channelName, null, 1,0, 0, 0.09999, 0.1, timeout); + actuator.set(); + actuator.set(); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#hasNext()}. + * @throws InterruptedException + */ + @Test + public void testHasNextOneStep() throws InterruptedException { + ChannelAccessLinearActuator actuator = new ChannelAccessLinearActuator(channelName, null, 1,0, 0, 0.09999, 0.1, timeout); + // Execute first set (because there is always a first move) + actuator.set(); + + // Check whether actuator returns that there is no next point + boolean next = actuator.hasNext(); + if(next){ + fail("There must be no next step"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#hasNext()}. + * @throws InterruptedException + */ + @Test + public void testHasNext() throws InterruptedException { + ChannelAccessLinearActuator actuator = new ChannelAccessLinearActuator(channelName, null, 1,0, 0, 10, 0.1, timeout); + + int count = 0; + int steps = (int) ((10-0)/0.1)+1; + while(actuator.hasNext()){ + actuator.set(); + count++; + } + + logger.fine("Actual steps: "+count+" - Needed steps: "+steps); + if(count != steps){ + fail("Actuator set more steps than specified"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#set()}. + * Test actuator move startend ... + * @throws InterruptedException + */ + @Test + public void testSetLoop() throws InterruptedException { + + List settings = new ArrayList(); + // start end stepsize + settings.add(new double[] {0, 10, 0.1}); + settings.add(new double[] {0, -1, 0.1}); + + + for(double[] svalue: settings){ + ChannelAccessLinearActuator actuator = new ChannelAccessLinearActuator(channelName, null, 1,0, svalue[0], svalue[1], svalue[2], timeout); + + int count =0; + while(actuator.hasNext()){ + actuator.set(); + count++; + } + + int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1; + + if(count != cnt){ + fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]"); + } + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#set()}. + * Test actuator move startend ... + * @throws CAException + */ + @Test + public void testReverse() throws CAException, InterruptedException { + + List settings = new ArrayList(); + // start end stepsize + settings.add(new double[] {0, 10, 0.1}); + settings.add(new double[] {0, -1, 0.1}); + + + for(double[] svalue: settings){ + ChannelAccessLinearActuator actuator = new ChannelAccessLinearActuator(channelName, null, 1,0, svalue[0], svalue[1], svalue[2], timeout); + actuator.init(); + + int count =0; + while(actuator.hasNext()){ + actuator.set(); + + // Check set value + double val = channel.getValue(); + logger.info("Value: "+val); + if(svalue[0] 0.001){ // 0.001 is precision + fail("Set value does not match actual value"); + } + } + else{ + if(Math.abs(val-(svalue[0]-(count*svalue[2]))) > 0.001){ // 0.001 is precision + fail("Set value does not match actual value"); + } + } + count++; + } + + int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1; + + if(count != cnt){ + fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]"); + } + + + actuator.reverse(); + actuator.init(); + count =0; + while(actuator.hasNext()){ + actuator.set(); + + // Check set value + double val = channel.getValue(); + logger.info("Value: "+val); + if(svalue[1] 0.001){ // 0.001 is precision + fail("Set value does not match actual value"); + } + } + else{ + if(Math.abs(val-(svalue[1]-(count*svalue[2]))) > 0.001){ // 0.001 is precision + fail("Set value does not match actual value"); + } + } + + count++; + } + + cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1; + + if(count != cnt){ + fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]"); + } + + + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#set()}. + * Test actuator move startend ... + * @throws CAException + */ + @Test + public void testReverseReset() throws CAException, InterruptedException { + + List settings = new ArrayList(); + // start end stepsize + settings.add(new double[] {0, 10, 0.1}); + settings.add(new double[] {0, -1, 0.1}); + + + for(double[] svalue: settings){ + ChannelAccessLinearActuator actuator = new ChannelAccessLinearActuator(channelName, null, 1,0, svalue[0], svalue[1], svalue[2], timeout); + actuator.init(); + + int count =0; + while(actuator.hasNext()){ + actuator.set(); + + // Check set value + double val = channel.getValue(); + logger.info("Value: "+val); + if(svalue[0] 0.001){ // 0.001 is precision + fail("Set value does not match actual value"); + } + } + else{ + if(Math.abs(val-(svalue[0]-(count*svalue[2]))) > 0.001){ // 0.001 is precision + fail("Set value does not match actual value"); + } + } + count++; + } + + int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1; + + if(count != cnt){ + fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]"); + } + + + actuator.reverse(); + actuator.reset(); + actuator.init(); + count =0; + while(actuator.hasNext()){ + actuator.set(); + + // Check set value + double val = channel.getValue(); + logger.info("Value: "+val); + if(svalue[0] 0.001){ // 0.001 is precision + fail("Set value does not match actual value"); + } + } + else{ + if(Math.abs(val-(svalue[0]-(count*svalue[2]))) > 0.001){ // 0.001 is precision + fail("Set value does not match actual value"); + } + } + count++; + } + + cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1; + + if(count != cnt){ + fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]"); + } + + + } + } + + + // TODO Check if actuator makes the required number of steps + // TODO Check if actuator makes the steps in the right direction + // TODO Check if actuator really blocks until motor/actuator is moved. + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#LinearActuatorChannelAccess(String, double, double, double)}. + * Check whether Exception is thrown if a negative step size is specified. + */ + @Test(expected=IllegalArgumentException.class) + public void testChannelAccessGPLinearActuatorNegativeStepSize() throws SocketException, CAException, Exception { + // Need to throw exception because of negative step size + new ChannelAccessLinearActuator(channelName, channelNameDone, doneValue, doneDelay, 0, 1, -0.1, timeout); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#LinearActuatorChannelAccess(String, double, double, double)}. + * Check whether Exception is thrown if a zero step size is specified. + */ + @Test(expected=IllegalArgumentException.class) + public void testChannelAccessGPLinearActuatorZeroStepSize() throws SocketException, CAException, Exception { + // Zero step size need to cause an exception + new ChannelAccessLinearActuator(channelName, channelNameDone, doneValue, doneDelay, 0, 1, 0, timeout); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#LinearActuatorChannelAccess(String, double, double, double)}. + * Check correct initialization + */ + public void testChannelAccessGPLinearActuator() throws SocketException, CAException, Exception { + // Zero step size need to cause an exception + new ChannelAccessLinearActuator(channelName, channelNameDone, doneValue, doneDelay, 0, 10, 1, timeout); + } + + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#set()}. + * @throws CAException + */ + @Test + public void testDoneSet() throws CAException, InterruptedException { + + ChannelAccessLinearActuator actuator = new ChannelAccessLinearActuator(channelName, channelNameDone, doneValue, doneDelay, 0, 0.09999, 0.1, timeout); + + // Simulate done channel + doneChannel.setValue(0); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(3000); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + + actuator.set(); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#set()}. + * @throws CAException + */ + @Test + public void testDoneDelay() throws CAException, InterruptedException { + + ChannelAccessLinearActuator actuator = new ChannelAccessLinearActuator(channelName, channelNameDone, doneValue, 1.5, 0, 1, 0.1, timeout); + + // Simulate done channel + doneChannel.setValue(1); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(1000); + doneChannel.setValue(0); + Thread.sleep(4000); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + + long start = System.currentTimeMillis(); + actuator.set(); + long end = System.currentTimeMillis(); + + logger.fine("Elapsed time: "+(end-start)); + if((end-start)<4000){ // Check whether all the moves took less than 6 seconds (thats the delay the done is set to 1) + fail("Done delay does not work"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#hasNext()}. + * Check whether the actuator throws an Exception if there is no next point but set() is called + * @throws CAException + */ + @Test(expected=IllegalStateException.class) + public void testDoneSetNoNext() throws CAException, InterruptedException { + + ChannelAccessLinearActuator actuator = new ChannelAccessLinearActuator(channelName, channelNameDone, doneValue, doneDelay, 0, 0.09999, 0.1, timeout); + + + // Simulate done channel + doneChannel.setValue(0); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(3000); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + + actuator.set(); + + + // Simulate done channel + doneChannel.setValue(0); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(3000); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + + actuator.set(); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#hasNext()}. + * @throws CAException + */ + @Test + public void testDoneHasNextOneStep() throws CAException, InterruptedException { + ChannelAccessLinearActuator actuator = new ChannelAccessLinearActuator(channelName, channelNameDone, doneValue, doneDelay, 0, 0.09999, 0.1, timeout); + + // Simulate done channel + doneChannel.setValue(0); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(3000); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + + // Execute first set (because there is always a first move) + actuator.set(); + + // Check whether actuator returns that there is no next point + boolean next = actuator.hasNext(); + if(next){ + fail("There must be no next step"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#hasNext()}. + * @throws CAException + */ + @Test + public void testDoneHasNext() throws CAException, InterruptedException { + ChannelAccessLinearActuator actuator = new ChannelAccessLinearActuator(channelName, channelNameDone, doneValue, doneDelay, 0, 10, 0.1, timeout); + + int count = 0; + int steps = (int) ((10-0)/0.1)+1; + while(actuator.hasNext()){ + + // Simulate done channel + doneChannel.setValue(0); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(10); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + + actuator.set(); + count++; + } + + logger.fine("Actual steps: "+count+" - Needed steps: "+steps); + if(count != steps){ + fail("Actuator set more steps than specified"); + } + } + + + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#set()}. + * Test actuator move startend ... + * @throws CAException + */ + @Test + public void testDoneSetLoop() throws CAException, InterruptedException { + + List settings = new ArrayList(); + // start end stepsize + settings.add(new double[] {0, 10, 0.1}); + settings.add(new double[] {0, -1, 0.1}); + + + for(double[] svalue: settings){ + ChannelAccessLinearActuator actuator = new ChannelAccessLinearActuator(channelName, channelNameDone, doneValue, doneDelay, svalue[0], svalue[1], svalue[2], timeout); + + int count =0; + while(actuator.hasNext()){ + + // Simulate done channel + doneChannel.setValue(0); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(10); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + + actuator.set(); + count++; + } + + int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1; + + if(count != cnt){ + fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]"); + } + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#set()}. + * Test whether the actuator returns if the actuator is already on the position it should be before the move ... + * (see issue XASEC-278) + * @throws CAException + */ + @Test + public void testMoveToActualPosition() throws CAException, InterruptedException { + + double start = 0; + double end = 2; + double stepSize = 1; + + channel.setValue(start); + + Thread.sleep(1000); + + logger.info("Current channel value: "+channel.getValue()); + + ChannelAccessLinearActuator actuator = new ChannelAccessLinearActuator(channelName, channelNameDone, doneValue, doneDelay, start, end, stepSize, timeout); + while(actuator.hasNext()){ + + // Simulate done channel + doneChannel.setValue(1); +// new Thread(new Runnable() { +// +// @Override +// public void run() { +// +// try { +// Thread.sleep(10); +//// doneChannel.setValue(1); +// } catch (Exception e) { +// } +// +// } +// }).start(); + + actuator.set(); + } + + } +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/ChannelAccessTableActuatorTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/ChannelAccessTableActuatorTest.java new file mode 100644 index 0000000..9c52859 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/ChannelAccessTableActuatorTest.java @@ -0,0 +1,476 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actors; + +import static org.junit.Assert.*; + +import java.util.logging.Logger; + +import gov.aps.jca.CAException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.TestChannels; +import ch.psi.fda.core.actors.ChannelAccessTableActuator; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * @author ebner + * + */ +public class ChannelAccessTableActuatorTest { + + // Get Logger + private static Logger logger = Logger.getLogger(ChannelAccessTableActuatorTest.class.getName()); + + private static final String channelName = TestChannels.BINARY_OUT; + private static final String channelNameDone = TestChannels.BINARY_OUT_TWO; + + private static final int doneValue = 1; + private static final double doneDelay = 0; + private static final Long timeout = 1800000l; // 30 minutes + + private ChannelBean channel; + private ChannelBean doneChannel; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + channel = ChannelBeanFactory.getFactory().createChannelBean(Double.class, channelName, false); + doneChannel = ChannelBeanFactory.getFactory().createChannelBean(Integer.class, channelNameDone, false); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#ChannelAccessTableActuator(java.lang.String, double[])}. + */ + @Test + public void testChannelAccessTableActuator() { + new ChannelAccessTableActuator(channelName, new double[]{1}, timeout); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#ChannelAccessTableActuator(java.lang.String, double[])}. + */ + @Test + public void testChannelAccessTableActuatorTimeout() { + // Negative timeout + boolean flag = false; + try{ + // Need to return IllegalArgumentException due to negative Timeout + new ChannelAccessTableActuator(channelName, new double[]{1}, -1l); + } + catch(IllegalArgumentException e){ + flag=true; + } + if(!flag){ + fail("Negative timeout is not handeled correctly"); + } + + // 0 timeout + flag=false; + try{ + // Need to return IllegalArgumentException + new ChannelAccessTableActuator(channelName, new double[]{1}, 0l); + } + catch(IllegalArgumentException e){ + flag=true; + } + if(!flag){ + fail("0 timeout is not handled correctly"); + } + + // Accept null timeout + new ChannelAccessTableActuator(channelName, new double[]{1}, null); + + // Accept positive timeout + new ChannelAccessTableActuator(channelName, new double[]{1}, 1l); + + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#ChannelAccessTableActuator(java.lang.String, double[])}. + * Test that constructor fails while providing a null table + */ + @Test(expected=IllegalArgumentException.class) + public void testChannelAccessTableActuatorNull() { + new ChannelAccessTableActuator(channelName, null, timeout); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#ChannelAccessTableActuator(java.lang.String, double[])}. + * Test that constructor fails while providing an empty table + */ + @Test(expected=IllegalArgumentException.class) + public void testChannelAccessTableActuatorEmptyTable() { + new ChannelAccessTableActuator(channelName, new double[0], timeout); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#set()}. + * @throws CAException + */ + @Test + public void testSet() throws CAException, InterruptedException { + double[] table = new double[]{1,2,3,4,5,6}; + ChannelAccessTableActuator actor = new ChannelAccessTableActuator(channelName, table, timeout); + + int count=0; + while(actor.hasNext()){ + actor.set(); + + if(channel.getValue()!=table[count]){ + fail("Channel value does not match expected set value"); + } + + count++; + } + + if(count != table.length){ + fail("Not all points given in the table were set"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#set()}. + * @throws InterruptedException + */ + @Test(expected=RuntimeException.class) + public void testSetFail() throws InterruptedException { + double[] table = new double[]{1}; + ChannelAccessTableActuator actor = new ChannelAccessTableActuator(channelName, table, timeout); + actor.set(); + + // This set() call has to fail with an RuntimeException + actor.set(); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#hasNext()}. + * @throws InterruptedException + */ + @Test + public void testHasNext() throws InterruptedException { + + double[] table = new double[]{1}; + ChannelAccessTableActuator actor = new ChannelAccessTableActuator(channelName, table, timeout); + actor.set(); + + boolean next = actor.hasNext(); + if(next){ + fail("There must be no next step"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#set()}. + * @throws CAException + */ + @Test + public void testReverse() throws CAException, InterruptedException { + double[] table = new double[]{1,2,3,4,5,6}; + ChannelAccessTableActuator actor = new ChannelAccessTableActuator(channelName, table, timeout); + actor.init(); + + int count=0; + while(actor.hasNext()){ + actor.set(); + + if(channel.getValue()!=table[count]){ + fail("Channel value does not match expected set value"); + } + + count++; + } + + if(count != table.length){ + fail("Not all points given in the table were set"); + } + + actor.reverse(); + actor.init(); + + count=0; + while(actor.hasNext()){ + actor.set(); + + if(channel.getValue()!=table[table.length-1-count]){ + fail("Channel value does not match expected set value"); + } + + count++; + } + + if(count != table.length){ + fail("Not all points given in the table were set"); + } + } + + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#set()}. + * @throws CAException + */ + @Test + public void testReverseReset() throws CAException, InterruptedException { + double[] table = new double[]{1,2,3,4,5,6}; + ChannelAccessTableActuator actor = new ChannelAccessTableActuator(channelName, table, timeout); + actor.init(); + + int count=0; + while(actor.hasNext()){ + actor.set(); + + if(channel.getValue()!=table[count]){ + fail("Channel value does not match expected set value"); + } + + count++; + } + + if(count != table.length){ + fail("Not all points given in the table were set"); + } + + actor.reverse(); + actor.reset(); + actor.init(); + + count=0; + while(actor.hasNext()){ + actor.set(); + + if(channel.getValue()!=table[count]){ + fail("Channel value does not match expected set value"); + } + + count++; + } + + if(count != table.length){ + fail("Not all points given in the table were set"); + } + } + + // DONE + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#ChannelAccessTableActuator(java.lang.String, double[])}. + */ + @Test + public void testDoneChannelAccessTableActuator() { + new ChannelAccessTableActuator(channelName, channelNameDone, doneValue, doneDelay, new double[]{1}, timeout); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#ChannelAccessTableActuator(java.lang.String, double[])}. + * Test that constructor fails while providing a null table + */ + @Test(expected=IllegalArgumentException.class) + public void testDoneChannelAccessTableActuatorNull() { + new ChannelAccessTableActuator(channelName, channelNameDone, doneValue, doneDelay, null, timeout); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#ChannelAccessTableActuator(java.lang.String, double[])}. + * Test that constructor fails while providing an empty table + */ + @Test(expected=IllegalArgumentException.class) + public void testDoneChannelAccessTableActuatorEmptyTable() { + new ChannelAccessTableActuator(channelName, channelNameDone, doneValue, doneDelay, new double[0], timeout); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#set()}. + * @throws CAException + */ + @Test + public void testDoneSet() throws CAException, InterruptedException { + double[] table = new double[]{1,2,3,4,5,6}; + ChannelAccessTableActuator actor = new ChannelAccessTableActuator(channelName, channelNameDone, doneValue, doneDelay, table, timeout); + + int count=0; + while(actor.hasNext()){ + // Simulate done channel + doneChannel.setValue(0); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(1000); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + actor.set(); + + if(channel.getValue()!=table[count]){ + fail("Channel value does not match expected set value"); + } + count++; + } + + if(count != table.length){ + fail("Not all points given in the table were set"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#set()}. + * @throws CAException + */ + @Test + public void testDoneDelay() throws CAException, InterruptedException { + double[] table = new double[]{1,2,3,4,5,6}; + ChannelAccessTableActuator actor = new ChannelAccessTableActuator(channelName, channelNameDone, doneValue, 0.1, table, timeout); + + int count=0; + long start = System.currentTimeMillis(); + while(actor.hasNext()){ + // Simulate done channel + doneChannel.setValue(1); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(10); + doneChannel.setValue(0); + Thread.sleep(1000); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + actor.set(); + + if(channel.getValue()!=table[count]){ + fail("Channel value does not match expected set value"); + } + count++; + } + long end = System.currentTimeMillis(); + + + if(count != table.length){ + fail("Not all points given in the table were set"); + } + + logger.fine("Elapsed time: "+(end-start)); + if((end-start)<6000){ // check whether all the moves took less than 6 seconds (thats the delay the done is set to 1) + fail("Done delay does not work"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#set()}. + * @throws CAException + */ + @Test(expected=RuntimeException.class) + public void testDoneSetFail() throws CAException, InterruptedException { + double[] table = new double[]{1}; + ChannelAccessTableActuator actor = new ChannelAccessTableActuator(channelName, channelNameDone, doneValue, doneDelay, table, timeout); + // Simulate done channel + doneChannel.setValue(0); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(3000); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + actor.set(); + + // This set() call has to fail with an RuntimeException + // Simulate done channel + doneChannel.setValue(0); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(3000); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + actor.set(); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#hasNext()}. + * @throws CAException + */ + @Test + public void testDoneHasNext() throws CAException, InterruptedException { + + double[] table = new double[]{1}; + ChannelAccessTableActuator actor = new ChannelAccessTableActuator(channelName, channelNameDone, doneValue, doneDelay, table, timeout); + + // Simulate done channel + doneChannel.setValue(0); + new Thread(new Runnable() { + + @Override + public void run() { + + try { + Thread.sleep(3000); + doneChannel.setValue(1); + } catch (Exception e) { + } + + } + }).start(); + actor.set(); + + boolean next = actor.hasNext(); + if(next){ + fail("There must be no next step"); + } + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/ComplexActorTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/ComplexActorTest.java new file mode 100644 index 0000000..25e8805 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/ComplexActorTest.java @@ -0,0 +1,91 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actors; + + +import static org.junit.Assert.fail; + +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.TestChannels; +import ch.psi.fda.core.actors.ChannelAccessLinearActuator; +import ch.psi.fda.core.actors.ComplexActuator; + +/** + * Prerequisites for this test are: + * All Actors that are used inside this test (except the ComplexActuator) need to be tested and + * fully compliant to their specification. + * @author ebner + * + */ +public class ComplexActorTest { + + // Get Logger + private static Logger logger = Logger.getLogger(ComplexActorTest.class.getName()); + + private static final Long timeout = 1800000l; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.actors.ComplexActuator#set()}. + * @throws InterruptedException + */ + @Test + public void testSet() throws InterruptedException { + + ComplexActuator actuator = new ComplexActuator(); + + actuator.getActors().add(new ChannelAccessLinearActuator(TestChannels.ANALOG_OUT, null, 1,0, 0, 0.09999, 0.1, timeout)); + actuator.getActors().add(new ChannelAccessLinearActuator(TestChannels.ANALOG_OUT, null, 1,0, 1, 2, 0.1, timeout)); + actuator.getActors().add(new ChannelAccessLinearActuator(TestChannels.ANALOG_OUT, null, 1,0, -1, -2, 0.1, timeout)); + + // Initialize actuator + actuator.init(); + + int count = 0; + while(actuator.hasNext()){ + actuator.set(); + count++; + } + + logger.fine("Execution count: "+count); + if(count!=23){ // There are 23 steps to do with the registered actors + fail("Actor did not perform all the steps"); + } + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/JythonFunctionTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/JythonFunctionTest.java new file mode 100644 index 0000000..2764580 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/JythonFunctionTest.java @@ -0,0 +1,97 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actors; + +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.core.scripting.JythonGlobalVariable; + +/** + * @author ebner + * + */ +public class JythonFunctionTest { + + // Get Logger + private static final Logger logger = Logger.getLogger(JythonFunctionTest.class.getName()); + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.actors.JythonFunction#calculate(double)}. + */ + @Test + public void testCalculate() { + Map map = new HashMap(); + + String script = "import math\ndef "+JythonFunction.ENTRY_FUNCTION_NAME+"(parameter):\n return math.sqrt(parameter)"; + JythonFunction f = new JythonFunction(script, map); + + double rval = f.calculate(4); + logger.info("Return value: "+rval); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.JythonFunction#calculate(double)}. + */ + @Test(expected=IllegalArgumentException.class) + public void testCalculateNoVariableMapping() { + Map map = new HashMap(); + String script = "import math\ndef "+JythonFunction.ENTRY_FUNCTION_NAME+"(parameter, a):\n return math.sqrt(parameter)+a.getValue()"; + new JythonFunction(script, map); // Must throw IllegalArgumentException !!!! + } + + /** + * Test method for {@link ch.psi.fda.core.actors.JythonFunction#calculate(double)}. + */ + @Test + public void testCalculateVariableMapping() { + Map map = new HashMap(); + JythonGlobalVariable v = new JythonGlobalVariable(); + v.setName("a"); + v.setValue(1.5); + map.put("a", v); + + String script = "import math\ndef "+JythonFunction.ENTRY_FUNCTION_NAME+"(parameter, a):\n return math.sqrt(parameter)+a.getValue()"; + JythonFunction f = new JythonFunction(script, map); + + double rval = f.calculate(4); + logger.info("Return value: "+rval); + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/PseudoActorSensorTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/PseudoActorSensorTest.java new file mode 100644 index 0000000..9c6e2cc --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/actors/PseudoActorSensorTest.java @@ -0,0 +1,138 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.actors; + +import static org.junit.Assert.*; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.core.actors.PseudoActuatorSensor; + +/** + * @author ebner + * + */ +public class PseudoActorSensorTest { + + private final String id = "xid"; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.actors.PseudoActuatorSensor#PseudoActor(int)}. + */ + @Test + public void testPseudoActor() { + new PseudoActuatorSensor(id, 1); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.PseudoActuatorSensor#PseudoActor(int)}. + */ + @Test(expected=IllegalArgumentException.class) + public void testPseudoActorFail() { + new PseudoActuatorSensor(id, 0); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.PseudoActuatorSensor#set()}. + */ + @Test + public void testSet() { + PseudoActuatorSensor actor = new PseudoActuatorSensor(id, 1); + actor.set(); + if(((Double)actor.read()).intValue()!=1){ + fail("Read value does not match set value"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.PseudoActuatorSensor#set()}. + * Check whether IllegalStateException is thrown if set() called more than allowed + */ + @Test(expected=IllegalStateException.class) + public void testSetFail() { + PseudoActuatorSensor actor = new PseudoActuatorSensor(id, 1); + actor.set(); + if(((Double)actor.read()).intValue()!=1){ + fail("Read value does not match set value"); + } + actor.set(); + } + + /** + * Test method for {@link ch.psi.fda.core.actors.PseudoActuatorSensor#hasNext()}. + */ + @Test + public void testHasNext() { + PseudoActuatorSensor actor = new PseudoActuatorSensor(id, 1); + + if(!actor.hasNext()){ + fail("Actor has no step when he should have one"); + } + actor.set(); + if(((Double)actor.read()).intValue()!=1){ + fail("Read value does not match set value"); + } + if(actor.hasNext()){ + fail("Actor has a step when he should not have one"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.actors.PseudoActuatorSensor#init()}. + */ + @Test + public void testInit() { + PseudoActuatorSensor actor = new PseudoActuatorSensor(id, 1); + actor.init(); + actor.set(); + if(((Double)actor.read()).intValue()!=1){ + fail("Read value does not match set value"); + } + if(actor.hasNext()){ + fail("Actor has a step when he should not have one"); + } + // Initialize actor to start + actor.init(); + actor.set(); + if(((Double)actor.read()).intValue()!=1){ + fail("Read value does not match set value"); + } + if(actor.hasNext()){ + fail("Actor has a step when he should not have one"); + } + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/collector/CollectorTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/collector/CollectorTest.java new file mode 100644 index 0000000..20a1b5e --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/collector/CollectorTest.java @@ -0,0 +1,236 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.collector; + +import static org.junit.Assert.*; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.core.collector.Collector; +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.ControlMessage; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; + +/** + * @author ebner + * + */ +public class CollectorTest { + + // Get Logger + private static Logger logger = Logger.getLogger(CollectorTest.class.getName()); + + class TestCollector implements Runnable{ + + private final DataQueue queue; + public TestCollector(DataQueue queue){ + this.queue = queue; + } + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + try { + while(true){ + Message m = queue.getQueue().take(); + if(m instanceof DataMessage){ + DataMessage x = (DataMessage) m; + logger.fine( x.toString() ); + } + else if(m instanceof ControlMessage){ + logger.fine("---- "+m.toString()+" ----"); + } + } + } catch (InterruptedException e) { + logger.log(Level.SEVERE, "An Exception occured while reading data from the data queue", e); + } + } + + } + + private BlockingQueue q1; + private BlockingQueue q2; + private BlockingQueue q3; + + private DataMessageMetadata m1; + private DataMessageMetadata m2; + private DataMessageMetadata m3; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Create blocking queues + q1 = new LinkedBlockingQueue(); + q2 = new LinkedBlockingQueue(); + q3 = new LinkedBlockingQueue(); + + m1 = new DataMessageMetadata(); + m1.getComponents().add(new ComponentMetadata("id2-0")); + m2 = new DataMessageMetadata(); + m2.getComponents().add(new ComponentMetadata("id1-0")); + m3 = new DataMessageMetadata(); + m3.getComponents().add(new ComponentMetadata("id0-0")); + + // Dimension y 1,2,3 + DataMessage m = new DataMessage(); + m.getData().add(1d); + q1.put(m); + m = new DataMessage(); + m.getData().add(2d); + q1.put(m); + m = new DataMessage(); + m.getData().add(3d); + q1.put(m); + q1.put(new EndOfStreamMessage()); + + // Dimension x 0.1,0.2 + m = new DataMessage(); + m.getData().add(0.1); + q2.put(m); + m = new DataMessage(); + m.getData().add(0.2); + q2.put(m); + q2.put(new EndOfStreamMessage()); + + m = new DataMessage(); + m.getData().add(0.1); + q2.put(m); + m = new DataMessage(); + m.getData().add(0.2); + q2.put(m); + q2.put(new EndOfStreamMessage()); + + m = new DataMessage(); + m.getData().add(0.1); + q2.put(m); + m = new DataMessage(); + m.getData().add(0.2); + q2.put(m); + q2.put(new EndOfStreamMessage()); + + // Dimension + m = new DataMessage(); + m.getData().add(0.01); + q3.put(m); + m = new DataMessage(); + m.getData().add(0.02); + q3.put(m); + q3.put(new EndOfStreamMessage()); + + m = new DataMessage(); + m.getData().add(0.01); + q3.put(m); + m = new DataMessage(); + m.getData().add(0.02); + q3.put(m); + q3.put(new EndOfStreamMessage()); + + m = new DataMessage(); + m.getData().add(0.01); + q3.put(m); + m = new DataMessage(); + m.getData().add(0.02); + q3.put(m); + q3.put(new EndOfStreamMessage()); + + m = new DataMessage(); + m.getData().add(0.01); + q3.put(m); + m = new DataMessage(); + m.getData().add(0.02); + q3.put(m); + q3.put(new EndOfStreamMessage()); + + m = new DataMessage(); + m.getData().add(0.01); + q3.put(m); + m = new DataMessage(); + m.getData().add(0.02); + q3.put(m); + q3.put(new EndOfStreamMessage()); + + m = new DataMessage(); + m.getData().add(0.01); + q3.put(m); + m = new DataMessage(); + m.getData().add(0.02); + q3.put(m); + q3.put(new EndOfStreamMessage()); + + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.collector.Collector#run()}. + * @throws InterruptedException + */ + @Test + public void testRun() throws InterruptedException { + Collector collector = new Collector(); + collector.getQueues().add(new DataQueue(q1, m1)); + collector.getQueues().add(new DataQueue(q2, m2)); + collector.getQueues().add(new DataQueue(q3, m3)); + + Thread t = new Thread(new TestCollector(collector.getOutQueue())); + t.start(); + + // Check component metadata of output queue + int c=2; + for(ComponentMetadata cm: collector.getOutQueue().getDataMessageMetadata().getComponents()){ + logger.info(cm.toString()); + if(cm.getDimension() != c){ + fail("Dimension number does not match required dimension number"); + } + if(! cm.getId().equals("id"+c+"-0")){ + fail("Id does not match required id"); + } + c--; + } + + collector.run(); + + // Wait some time to ensure that collector was able to finish processing + Thread.sleep(2000); + // Execute collector via the ExecutorService framework +// ExecutorService executor = Executors.newCachedThreadPool(); +// executor.execute(collector); + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/guard/ChannelAccessGuardTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/guard/ChannelAccessGuardTest.java new file mode 100644 index 0000000..14fa0ee --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/guard/ChannelAccessGuardTest.java @@ -0,0 +1,111 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.guard; + +import static org.junit.Assert.*; + +import gov.aps.jca.CAException; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.TestChannels; +import ch.psi.fda.core.Guard; +import ch.psi.fda.core.guard.ChannelAccessGuard; +import ch.psi.fda.core.guard.ChannelAccessGuardCondition; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * @author ebner + * + */ +public class ChannelAccessGuardTest { + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.guard.ChannelAccessGuard#check()}. + * @throws InterruptedException + * @throws CAException + */ + @Test + public void testCheck() throws InterruptedException, CAException { + String guardChannel = TestChannels.ANALOG_OUT; + Integer channelOkValue = 10; + List conditions = new ArrayList(); + conditions.add(new ChannelAccessGuardCondition(guardChannel, channelOkValue)); + Guard guard = new ChannelAccessGuard(conditions ); + + ChannelBean b = ChannelBeanFactory.getFactory().createChannelBean(Integer.class, guardChannel, false); + b.setValue(channelOkValue); + + guard.init(); + + if(!guard.check()){ + fail("Guard not in correct state"); + } + + b.setValue(channelOkValue+1); + b.setValue(channelOkValue); + + Thread.sleep(1000); + + // Check whether guard is really on NOT OK + if(guard.check()){ + fail("Guard not in correct state"); + } + + b.setValue(channelOkValue); + + // Check if after init the guard is ok again + guard.init(); + + if(!guard.check()){ + fail("Guard not in correct state"); + } + + // Check if after init the guard is not ok + b.setValue(channelOkValue+1); + guard.init(); + + if(guard.check()){ + fail("Guard not in correct state"); + } + + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/loops/ActorSensorLoopTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/loops/ActorSensorLoopTest.java new file mode 100644 index 0000000..7ffac80 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/loops/ActorSensorLoopTest.java @@ -0,0 +1,374 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.loops; + +import static org.junit.Assert.*; + +import gov.aps.jca.CAException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.TestChannels; +import ch.psi.fda.core.Actor; +import ch.psi.fda.core.Guard; +import ch.psi.fda.core.actions.Delay; +import ch.psi.fda.core.actors.ChannelAccessLinearActuator; +import ch.psi.fda.core.guard.ChannelAccessGuard; +import ch.psi.fda.core.guard.ChannelAccessGuardCondition; +import ch.psi.fda.core.loops.ActorSensorLoop; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; +import ch.psi.fda.core.messages.Message; +import ch.psi.fda.core.sensors.ChannelAccessDoubleArraySensor; +import ch.psi.fda.core.sensors.ChannelAccessDoubleSensor; +import ch.psi.fda.core.sensors.ChannelAccessStringSensor; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * @author ebner + * + */ +public class ActorSensorLoopTest { + + // Get Logger + private static Logger logger = Logger.getLogger(ActorSensorLoopTest.class.getName()); + + private static final String boChannel = TestChannels.BINARY_OUT; + private static final String aoChannel = TestChannels.ANALOG_OUT; + private static final String wfChannel = TestChannels.DOUBLE_WAVEFORM; + private static final Long timeout = 1800000l; // 30 minutes + + private ActorSensorLoop loopOne; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + + loopOne = new ActorSensorLoop(); + + ChannelAccessLinearActuator a = new ChannelAccessLinearActuator(aoChannel, null, 1,0, 0, 10, 0.1, timeout); // Positioner + ChannelAccessDoubleSensor s = new ChannelAccessDoubleSensor("id0", boChannel); // Positioner Readback + ChannelAccessDoubleSensor s1 = new ChannelAccessDoubleSensor("id1", aoChannel); // Scalar Detector + ChannelAccessStringSensor s1string = new ChannelAccessStringSensor("id3", boChannel+".NAME"); // Scalar String Detector + ChannelAccessDoubleArraySensor s2 = new ChannelAccessDoubleArraySensor("id2", wfChannel, 10); + + loopOne.getActors().add(a); + loopOne.getSensors().add(s); + loopOne.getSensors().add(s1); + loopOne.getSensors().add(s1string); + loopOne.getSensors().add(s2); + + loopOne.prepare(); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + loopOne.destroy(); + } + + /** + * Test method for {@link ch.psi.fda.core.loops.ActorSensorLoop#execute()}. + * @throws InterruptedException + */ + @Test + public void testExecute() throws InterruptedException { + + final BlockingQueue dataQueue = loopOne.getDataQueue().getQueue(); + Thread t = new Thread(new Runnable() { + + @Override + public void run() { + try { + while(true){ + Message m = dataQueue.take(); + if(m instanceof DataMessage){ + DataMessage x = (DataMessage) m; + logger.fine( x.toString() ); + } + } + } catch (InterruptedException e) { + logger.log(Level.SEVERE, "An Exception occured while reading data from the data queue", e); + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + }); + + t.start(); + loopOne.execute(); +// Thread.sleep(3000); +// t.interrupt(); + } + + /** + * Test method for {@link ch.psi.fda.core.loops.ActorSensorLoop#getDataMessageMetadata()}. + */ + @Test + public void testGetDataMessageMetadata(){ + ActorSensorLoop loop = new ActorSensorLoop(); + + int numberOfSensors = 2; + ChannelAccessDoubleSensor s1 = new ChannelAccessDoubleSensor("id0", boChannel); // Positioner Readback + ChannelAccessDoubleSensor s2 = new ChannelAccessDoubleSensor("id1", aoChannel); // Scalar Detector + loop.getSensors().add(s1); + loop.getSensors().add(s2); + + DataMessageMetadata m = loop.getDataQueue().getDataMessageMetadata(); + + + if(m.getComponents().size() != numberOfSensors){ + fail("Loop returned wrong number of components inside the data message metadata"); + } + + boolean fail = false; + for(int x=0;x a = new ChannelAccessLinearActuator(aoChannel, null, 1,0, 0, 1.5, 0.1,timeout); // Positioner + + ChannelAccessDoubleSensor s = new ChannelAccessDoubleSensor("id0", aoChannel); // Positioner Readback + ChannelAccessDoubleSensor s1 = new ChannelAccessDoubleSensor("id1", boChannel); // Scalar Detector + ChannelAccessDoubleArraySensor s2 = new ChannelAccessDoubleArraySensor("id2", wfChannel, 10); + + loop.getActors().add(a); + loop.getPostActorActions().add(new Delay(500)); + loop.getSensors().add(s); + loop.getSensors().add(s1); + loop.getSensors().add(s2); + + // Guard + final Integer okValue = 0; + List conditions = new ArrayList(); + conditions.add(new ChannelAccessGuardCondition(boChannel, new Integer(0))); + Guard guard = new ChannelAccessGuard(conditions); + loop.setGuard(guard); + + final ChannelBean b = ChannelBeanFactory.getFactory().createChannelBean(Integer.class, boChannel, false); + + Thread tguard = new Thread(new Runnable() { + + @Override + public void run() { + try { + // Wait some seconds and set channel to ok + Thread.sleep(1000); + b.setValue(okValue); + + // Set channel to not ok + Thread.sleep(1000); + b.setValue(okValue+1); + + // Wait some seconds and set channel to ok again + Thread.sleep(4000); + b.setValue(okValue); + } catch (Exception e) { + logger.log(Level.SEVERE, "An Exception occured while setting guard channel", e); + } + } + }); + + final BlockingQueue dataQueue = loop.getDataQueue().getQueue(); + Thread t = new Thread(new Runnable() { + + @Override + public void run() { + try { + while(true){ + Message m = dataQueue.take(); + if(m instanceof DataMessage){ + DataMessage x = (DataMessage) m; + logger.fine( x.toString() ); + } + } + } catch (InterruptedException e) { + logger.log(Level.SEVERE, "An Exception occured while reading data from the data queue", e); + } + } + }); + + // Set the guard channel to not OK + b.setValue(okValue+1); + + t.start(); + tguard.start(); + loop.prepare(); + loop.execute(); + + logger.info("End of test"); + +// Thread.sleep(3000); +// t.interrupt(); + } + + + + @Test + public void testParallelSet() throws InterruptedException, CAException { + + final int steps = 2; + final HashMap timestamps = new HashMap(); + + ActorSensorLoop loop = new ActorSensorLoop(); + + ChannelAccessLinearActuator a = new ChannelAccessLinearActuator(aoChannel, null, 1,0, 0, (steps*0.1), 0.1, timeout); // Positioner + + ChannelAccessDoubleSensor s = new ChannelAccessDoubleSensor("id0", aoChannel); // Positioner Readback + + loop.getActors().add(a); + loop.getActors().add(new Actor() { + int c = 0; + @Override + public void set() { + logger.info("Start actor 1"); + timestamps.put("sa1", System.currentTimeMillis()); + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + logger.info("Done actor 1"); + timestamps.put("da1", System.currentTimeMillis()); + c++; + } + + @Override + public void reverse() { + } + + @Override + public void reset() { + } + + @Override + public void init() { + } + + @Override + public boolean hasNext() { + if(ctimestamps.get("sa2")) ){ + fail("Done 1 occured before start 2"); + } + + if(! (timestamps.get("da2")>timestamps.get("sa1")) ){ + fail("Done 2 occured before start 1"); + } + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/loops/OTFLoopTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/loops/OTFLoopTest.java new file mode 100644 index 0000000..50d92a8 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/loops/OTFLoopTest.java @@ -0,0 +1,255 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.loops; + +import static org.junit.Assert.*; + +import gov.aps.jca.CAException; + +import java.util.concurrent.BlockingQueue; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.core.actors.OTFActuator; +import ch.psi.fda.core.loops.OTFLoop; +import ch.psi.fda.core.messages.ControlMessage; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; +import ch.psi.fda.core.messages.Message; +import ch.psi.fda.core.sensors.MillisecondTimestampSensor; +import ch.psi.fda.core.sensors.OTFNamedChannelSensor; +import ch.psi.fda.core.sensors.OTFReadbackSensor; +import ch.psi.fda.core.sensors.OTFScalerChannelSensor; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * @author ebner + * + */ +public class OTFLoopTest { + + // Get Logger + private static Logger logger = Logger.getLogger(OTFLoopTest.class.getName()); + + class TestCollector implements Runnable{ + + private final BlockingQueue queue; + public TestCollector(BlockingQueue queue){ + this.queue = queue; + } + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + try { + while(true){ + Message m = queue.take(); + if(m instanceof DataMessage){ + DataMessage x = (DataMessage) m; + logger.fine( x.toString() ); + } + else if(m instanceof ControlMessage){ + logger.fine("---- "+m.toString()+" ----"); + } + } + } catch (InterruptedException e) { + logger.log(Level.SEVERE, "An Exception occured while reading data from the data queue", e); + } + } + + } + + private static final String motor = "MTEST-HW3:MOT1"; + private static final String channelPrefix = "MTEST-HW3-OTFX"; + private static final String server = "slsyoke1.psi.ch"; + private static final String share = "/usr/local/nfsshare"; + private static final String smbShare = "smb://:@slsyoke1.psi.ch/otftemp/"; + + + private OTFLoop loopZigZag; + private OTFLoop loop; + private ChannelBean statusChannel; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + + statusChannel = ChannelBeanFactory.getFactory().createChannelBean(Integer.class, channelPrefix+":USTAT", false); + + OTFActuator actor = new OTFActuator("id", motor, null, 0, 8, 0.5, 0.5, 0); + OTFReadbackSensor s1 = new OTFReadbackSensor("id0"); + OTFScalerChannelSensor s2 = new OTFScalerChannelSensor("id1", 0); + OTFScalerChannelSensor s3 = new OTFScalerChannelSensor("id2", 1); + MillisecondTimestampSensor s4 = new MillisecondTimestampSensor("id3"); + OTFNamedChannelSensor s5 = new OTFNamedChannelSensor("id4", "MTEST-HW3-AI1:AI_01"); + + + // ZigZag loop + loopZigZag = new OTFLoop(channelPrefix, server, share, smbShare, true); + loopZigZag.setActor(actor); + loopZigZag.getSensors().add(s1); + loopZigZag.getSensors().add(s2); + loopZigZag.getSensors().add(s3); + loopZigZag.getSensors().add(s4); + loopZigZag.getSensors().add(s5); + + + // Normal loop + loop = new OTFLoop(channelPrefix, server, share, smbShare, false); + loop.setActor(actor); + loop.getSensors().add(s1); + loop.getSensors().add(s2); + loop.getSensors().add(s3); + loop.getSensors().add(s4); + loop.getSensors().add(s5); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.loops.OTFLoop#OTFLoop(java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean)}. + */ + @Test + public void testOTFLoop() { + fail("Not yet implemented"); + } + + /** + * Test method for {@link ch.psi.fda.core.loops.OTFLoop#execute()}. + * Test ordinary 1D OTF scan + * @throws CAException + */ + @Test + public void testExecute() throws CAException, InterruptedException { + + Thread t = new Thread(new TestCollector(loop.getDataQueue().getQueue())); + t.start(); + + loop.prepare(); + + loop.execute(); + + loop.cleanup(); + + if(statusChannel.getValue()!=1){ + fail("OTF C Logic status is not on INACTIVE"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.loops.OTFLoop#execute()}. + * Test OTF ZigZag mode + * @throws CAException + */ + @Test + public void testExecuteZigZag() throws CAException, InterruptedException { + + Thread t = new Thread(new TestCollector(loopZigZag.getDataQueue().getQueue())); + t.start(); + + loopZigZag.prepare(); + + loopZigZag.execute(); + loopZigZag.execute(); + + loopZigZag.cleanup(); + + if(statusChannel.getValue()!=1){ + fail("OTF C Logic status is not on INACTIVE"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.loops.OTFLoop#execute()}. + * Test abort functionality while executing an OTF scan + * @throws CAException + */ + @Test + public void testExecuteAbort() throws CAException, InterruptedException { + + // Data collector thread + Thread t = new Thread(new TestCollector(loop.getDataQueue().getQueue())); + t.start(); + + // Thread to simulate asynchronous abort operation + Thread t1 = new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(6000); // Wait some seconds before aborting the loop + loop.abort(); + } catch (InterruptedException e) { + logger.log(Level.SEVERE, "An Exception occured while testing the abort functionality", e); + } + } + }); + t1.start(); + + loop.prepare(); + + loop.execute(); + + loop.cleanup(); + + if(statusChannel.getValue()!=1){ + fail("OTF C Logic status is not on INACTIVE"); + } + } + + /** + * Test method for {@link ch.psi.fda.core.loops.ActorSensorLoop#getDataMessageMetadata()}. + */ + @Test + public void testGetDataMessageMetadata(){ + DataMessageMetadata m = loop.getDataQueue().getDataMessageMetadata(); + + int numberOfSensors = 5; + if(m.getComponents().size() != numberOfSensors){ + fail("Loop returned wrong number of components inside the data message metadata"); + } + + boolean fail = false; + for(int x=0;x. + * + */ + +package ch.psi.fda.core.loops.cr; + + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * @author ebner + * + */ +public class CrlogicLoopTest { + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + @Test + public void testExecute(){ + + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/loops/cr/ParallelCrlogicStreamMergeTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/loops/cr/ParallelCrlogicStreamMergeTest.java new file mode 100644 index 0000000..0d6d251 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/loops/cr/ParallelCrlogicStreamMergeTest.java @@ -0,0 +1,136 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.loops.cr; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; + +/** + * @author ebner + * + */ +public class ParallelCrlogicStreamMergeTest { + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.loops.cr.ParallelCrlogicStreamMerge#merge()}. + * @throws InterruptedException + */ + @Test + public void testMerge() throws InterruptedException { + + BlockingQueue dataQueue = new LinkedBlockingQueue(); + DataMessage dm = new DataMessage(); + dm.getData().add(0.0035d); + dm.getData().add(10.000000123); + dataQueue.add(dm); + + dm = new DataMessage(); + dm.getData().add(0.015); + dm.getData().add(10.000000143); + dataQueue.add(dm); + + dm = new DataMessage(); + dm.getData().add(0.026); + dm.getData().add(10.000000163); + dataQueue.add(dm); + + dataQueue.add(new EndOfStreamMessage()); + + DataMessageMetadata dmm = new DataMessageMetadata(); + dmm.getComponents().add(new ComponentMetadata("cractuator")); + dmm.getComponents().add(new ComponentMetadata("tstamp")); + DataQueue dqueue = new DataQueue(dataQueue, dmm); + + + BlockingQueue dataQueue2 = new LinkedBlockingQueue(); + DataMessage dm2 = new DataMessage(); + dm2.getData().add(9000d); + dm2.getData().add(122d); + dm2.getData().add(0.1d); + dataQueue2.add(dm2); + + dm2 = new DataMessage(); + dm2.getData().add(10000d); + dm2.getData().add(122d); + dm2.getData().add(1d); + dataQueue2.add(dm2); + + dm2 = new DataMessage(); + dm2.getData().add(10000d); + dm2.getData().add(153d); + dm2.getData().add(2d); + dataQueue2.add(dm2); + + dm2 = new DataMessage(); + dm2.getData().add(10000d); + dm2.getData().add(162d); + dm2.getData().add(3d); + dataQueue2.add(dm2); + + dataQueue2.add(new EndOfStreamMessage()); + + DataMessageMetadata dmm2 = new DataMessageMetadata(); + dmm2.getComponents().add(new ComponentMetadata("milli")); + dmm2.getComponents().add(new ComponentMetadata("nano")); + dmm2.getComponents().add(new ComponentMetadata("sensor1")); + + DataQueue dqueue2 = new DataQueue(dataQueue2, dmm2); + + + + ParallelCrlogicStreamMerge streamMerge = new ParallelCrlogicStreamMerge(dqueue, dqueue2); + streamMerge.merge(); + + // Print merged queue + BlockingQueue queue = streamMerge.getDataQueue().getQueue(); + Message m = queue.take(); + while(! (m instanceof EndOfStreamMessage)){ + System.out.println(m.toString()); + m = queue.take(); + } + + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/loops/cr/ParallelCrlogicTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/loops/cr/ParallelCrlogicTest.java new file mode 100644 index 0000000..dd5545c --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/loops/cr/ParallelCrlogicTest.java @@ -0,0 +1,152 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.loops.cr; + + +import gov.aps.jca.CAException; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.core.Sensor; +import ch.psi.fda.core.actors.OTFActuator; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; +import ch.psi.fda.core.sensors.ChannelAccessDoubleSensor; +import ch.psi.fda.core.sensors.MillisecondTimestampSensor; +import ch.psi.fda.core.sensors.OTFNamedChannelSensor; +import ch.psi.fda.core.sensors.OTFScalerChannelSensor; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * @author ebner + * + */ +public class ParallelCrlogicTest { + + // Get Logger + private static final Logger logger = Logger.getLogger(ParallelCrlogicTest.class.getName()); + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + @Test + public void testExecute() throws InterruptedException, CAException{ + + String prefix = "MTEST-HW3-CRL"; + String prefixScaler = "MTEST-HW3:JS"; + String server = "slsyoke1.psi.ch"; + String share = "/usr/local/nfsshare"; + String smbShare = "smb://:@sdc.psi.ch/otftemp/"; + boolean zigZag = false; + + String name = "MTEST-HW3:MOT1"; + String readback = null; + double start = 0; + double end = 2; + double stepSize = 0.01; + double integrationTime = 0.01; + double additionalBacklash = 0; + + List sensors = new ArrayList(); + ChannelAccessDoubleSensor s2 = new ChannelAccessDoubleSensor("mot1", name+".RVAL"); + ChannelAccessDoubleSensor s1 = new ChannelAccessDoubleSensor("mot1", name+".RBV"); + + sensors.add(s1); + sensors.add(s2); + + ScrlogicLoop scrlogic = new ScrlogicLoop(sensors); + + + CrlogicLoop crlogic = new CrlogicLoop(prefix, server, share, smbShare, zigZag); + crlogic.setActor(new OTFActuator("cmot", name, readback, start, end, stepSize, integrationTime, additionalBacklash)); + crlogic.getSensors().add(new OTFNamedChannelSensor("trigger", "TRIGGER0")); + crlogic.getSensors().add(new OTFScalerChannelSensor("scaler0", 0)); + crlogic.getSensors().add(new OTFScalerChannelSensor("scaler1", 1)); + crlogic.getSensors().add(new MillisecondTimestampSensor("timestamp")); + + + // Initialize scaler template + VSC16ScalerChannelsTemplate scalertemplate = new VSC16ScalerChannelsTemplate(); + ChannelBeanFactory.getFactory().createChannelBeans(scalertemplate, prefixScaler); + + + ParallelCrlogic pcrlogic = new ParallelCrlogic(crlogic, scrlogic); + + logger.info("Start scaler"); + scalertemplate.getCommand().setValueNoWait(VSC16ScalerChannelsTemplate.Command.Count.ordinal()); + + pcrlogic.prepare(); + pcrlogic.execute(); + pcrlogic.cleanup(); + + logger.info("Stop scaler"); + scalertemplate.getCommand().setValue(VSC16ScalerChannelsTemplate.Command.Done.ordinal()); + + System.out.println("PARALLEL CRLOGIC data:"); + BlockingQueue queue = pcrlogic.getDataQueue().getQueue(); + Message m = queue.take(); + while(! (m instanceof EndOfStreamMessage)){ + System.out.println(m.toString()); + m = queue.take(); + } + + // Destroy scaler template + ChannelBeanFactory.getFactory().destroyChannelBeans(scalertemplate); + + pcrlogic.destroy(); + + +// System.out.println("CRLOGIC data:"); +// BlockingQueue queue = crlogic.getDataQueue().getQueue(); +// Message m = queue.take(); +// while(! (m instanceof EndOfStreamMessage)){ +// System.out.println(m.toString()); +// m = queue.take(); +// } +// +// +// System.out.println("SCRLOGIC data:"); +// queue = scrlogic.getDataQueue().getQueue(); +// m = queue.take(); +// while(! (m instanceof EndOfStreamMessage)){ +// System.out.println(m.toString()); +// m = queue.take(); +// } + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/loops/cr/ScrlogicLoopTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/loops/cr/ScrlogicLoopTest.java new file mode 100644 index 0000000..d77754a --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/loops/cr/ScrlogicLoopTest.java @@ -0,0 +1,96 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.loops.cr; + +import gov.aps.jca.CAException; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.core.Sensor; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; +import ch.psi.fda.core.sensors.ChannelAccessDoubleSensor; + +/** + * @author ebner + * + */ +public class ScrlogicLoopTest { + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.loops.cr.ScrlogicLoop#execute()}. + * @throws InterruptedException + * @throws CAException + */ + @Test + public void testExecute() throws InterruptedException, CAException { + + System.out.println("For this test the motor MTEST-HW3:MOT1 need to be moved manually"); + + List sensors = new ArrayList(); + ChannelAccessDoubleSensor s2 = new ChannelAccessDoubleSensor("mot1", "MTEST-HW3:MOT1.RVAL"); + ChannelAccessDoubleSensor s1 = new ChannelAccessDoubleSensor("mot1", "MTEST-HW3:MOT1"); + + sensors.add(s1); + sensors.add(s2); + + ScrlogicLoop logic = new ScrlogicLoop(sensors); + + + + logic.prepare(); + logic.execute(); + + // Wait some time until + Thread.sleep(10000); + + logic.abort(); + + logic.destroy(); + + BlockingQueue queue = logic.getDataQueue().getQueue(); + Message m = queue.take(); + while(! (m instanceof EndOfStreamMessage)){ + System.out.println(m.toString()); + m = queue.take(); + } + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/manipulator/ManipulatorTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/manipulator/ManipulatorTest.java new file mode 100644 index 0000000..11e99a1 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/manipulator/ManipulatorTest.java @@ -0,0 +1,487 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.manipulator; + +import static org.junit.Assert.*; + +import gov.aps.jca.CAException; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import ch.psi.fda.core.manipulator.JythonManipulation; +import ch.psi.fda.core.manipulator.Manipulation; +import ch.psi.fda.core.manipulator.Manipulator; +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; +import ch.psi.fda.core.scripting.JythonParameterMapping; +import ch.psi.fda.core.scripting.JythonParameterMappingChannel; +import ch.psi.fda.core.scripting.JythonParameterMappingID; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * @author ebner + * + */ +public class ManipulatorTest { + + // Get Logger + private static Logger logger = Logger.getLogger(ManipulatorTest.class.getName()); + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.manipulator.Manipulator#Manipulator()}. + */ + @Test(expected=IllegalArgumentException.class) + public void testConstructor() { + DataMessageMetadata dmm = new DataMessageMetadata(); + DataQueue inQueue = new DataQueue(new LinkedBlockingQueue(), dmm); + + String id="computedId"; + String script = "import math\ndef process(o):\n return math.cos(10.0) + math.sin(o)"; + List mapping = new ArrayList(); + mapping.add(new JythonParameterMappingID("o", "myid")); + JythonManipulation manipulation = new JythonManipulation(id, script, mapping); + + // This constructor need to throw an IllegalArgumentException as there is no component + // id "myid" which is expected in the mapping + List manipulations = new ArrayList(); + manipulations.add(manipulation); + new Manipulator(inQueue, manipulations); + + } + + @Test + public void testConstructorNoMappingNoParam() { + DataMessageMetadata dmm = new DataMessageMetadata(); + DataQueue inQueue = new DataQueue(new LinkedBlockingQueue(), dmm); + + DataMessage m = new DataMessage(); + m.getData().add(10d); + m.getData().add(0.2d); + inQueue.getQueue().add(m); + inQueue.getQueue().add(new EndOfStreamMessage()); + + String id="cid"; + String script = "import math\ndef process():\n return 0.0"; + List mapping = new ArrayList(); + mapping.add(new JythonParameterMappingID("o", "myid")); + JythonManipulation manipulation = new JythonManipulation(id, script, mapping); + + List manipulations = new ArrayList(); + manipulations.add(manipulation); + new Manipulator(inQueue, manipulations); + // Expect IllegalArgument Exception as there is no mapping for the parameter c + } + + @Test + public void testConstructorMappingNoParam() { + DataMessageMetadata dmm = new DataMessageMetadata(); + dmm.getComponents().add(new ComponentMetadata("myid")); + dmm.getComponents().add(new ComponentMetadata("myid2")); + DataQueue inQueue = new DataQueue(new LinkedBlockingQueue(), dmm); + + DataMessage m = new DataMessage(); + m.getData().add(10d); + m.getData().add(0.2d); + inQueue.getQueue().add(m); + inQueue.getQueue().add(new EndOfStreamMessage()); + + String id="cid"; + String script = "import math\ndef process():\n return 0.0"; + List mapping = new ArrayList(); + mapping.add(new JythonParameterMappingID("o", "myid")); + JythonManipulation manipulation = new JythonManipulation(id, script, mapping); + + List manipulations = new ArrayList(); + manipulations.add(manipulation); + new Manipulator(inQueue, manipulations); + // Expect IllegalArgument Exception as there is no mapping for the parameter c + } + + /** + * Test method for {@link ch.psi.fda.core.manipulator.Manipulator#Manipulator()}. + */ + @Test(expected=IllegalArgumentException.class) + public void testConstructorNoMapping() { + DataMessageMetadata dmm = new DataMessageMetadata(); + dmm.getComponents().add(new ComponentMetadata("myid")); + dmm.getComponents().add(new ComponentMetadata("myid2")); + DataQueue inQueue = new DataQueue(new LinkedBlockingQueue(), dmm); + + DataMessage m = new DataMessage(); + m.getData().add(10d); + m.getData().add(0.2d); + inQueue.getQueue().add(m); + inQueue.getQueue().add(new EndOfStreamMessage()); + + String id="cid"; + String script = "import math\ndef process(o ,c):\n return math.cos(c) + math.sin(o)"; + List mapping = new ArrayList(); + mapping.add(new JythonParameterMappingID("o", "myid")); + JythonManipulation manipulation = new JythonManipulation(id, script, mapping); + + List manipulations = new ArrayList(); + manipulations.add(manipulation); + new Manipulator(inQueue, manipulations); + // Expect IllegalArgument Exception as there is no mapping for the parameter c + } + + /** + * Test method for {@link ch.psi.fda.core.manipulator.Manipulator#run()}. + * @throws InterruptedException + */ + @Test + public void testRun() throws InterruptedException { + DataMessageMetadata dmm = new DataMessageMetadata(); + dmm.getComponents().add(new ComponentMetadata("myid")); + DataQueue inQueue = new DataQueue(new LinkedBlockingQueue(), dmm); + + DataMessage m = new DataMessage(); + m.getData().add(10d); + inQueue.getQueue().add(m); + inQueue.getQueue().add(new EndOfStreamMessage()); + + String id="cid"; + String script = "import math\ndef process(o):\n return math.cos(10.0) + math.sin(o)"; + List mapping = new ArrayList(); + mapping.add(new JythonParameterMappingID("o", "myid")); + JythonManipulation manipulation = new JythonManipulation(id, script, mapping); + + List manipulations = new ArrayList(); + manipulations.add(manipulation); + Manipulator manipulator = new Manipulator(inQueue, manipulations); + + // Check whether output queue message structur complies to expected one + DataMessageMetadata outMeta = manipulator.getOutQueue().getDataMessageMetadata(); + + // Test whether only the expected components are within the outgoing queue + if(outMeta.getComponents().size()!=2){ + fail("There are more than the expected components in the outgoing message"); + } + + // Test whether the id of the first component matches the expected id + if(!outMeta.getComponents().get(0).getId().equals("myid")){ + fail("Id of the first component does not match the expected id 'myid'"); + } + + // Test whether the id of the second component (which was computed) matches the expected id + if(!outMeta.getComponents().get(1).getId().equals("cid")){ + fail("Id of the second component does not match the expected id 'cid'"); + } + + + manipulator.run(); + + Message message = manipulator.getOutQueue().getQueue().take(); + while(!(message instanceof EndOfStreamMessage)){ + + logger.info(message.toString()); + + if(message instanceof DataMessage){ + DataMessage dm = (DataMessage) message; + dm.getData().get(0); + double res = ((Double)dm.getData().get(1)) - (Math.cos(10.0)+Math.sin(((Double)dm.getData().get(0)))); + if( Math.abs(res) > 0.000000001){ + fail("Calculation failed"); + } + } + + message = manipulator.getOutQueue().getQueue().take(); + } + + logger.info(""+(Math.cos(10.0)+Math.sin(10))); + } + + /** + * Test method for {@link ch.psi.fda.core.manipulator.Manipulator#run()}. + * @throws InterruptedException + */ + @Ignore + @Test + public void testRunLongTimeTest() throws InterruptedException { + DataMessageMetadata dmm = new DataMessageMetadata(); + dmm.getComponents().add(new ComponentMetadata("myid")); + final DataQueue inQueue = new DataQueue(new LinkedBlockingQueue(1000), dmm); + + Thread tf = new Thread(new Runnable() { + + @Override + public void run() { + try{ + for(Double i=0d;i<1000000;i++){ + DataMessage m = new DataMessage(); + m.getData().add(i); + inQueue.getQueue().put(m); + // try { + // Thread.sleep(1); + // } catch (InterruptedException e) { + // } + } + inQueue.getQueue().put(new EndOfStreamMessage()); + } + catch(InterruptedException e){ + e.printStackTrace(); + } + } + }); + + + String id="cid"; + String script = "import math\ndef process(o):\n return math.cos(10.0) + math.sin(o)"; + List mapping = new ArrayList(); + mapping.add(new JythonParameterMappingID("o", "myid")); + JythonManipulation manipulation = new JythonManipulation(id, script, mapping); + + List manipulations = new ArrayList(); + manipulations.add(manipulation); + Manipulator manipulator = new Manipulator(inQueue, manipulations); + + + + Thread t = new Thread(manipulator); + + final DataQueue outQueue = manipulator.getOutQueue(); + + Thread tp = new Thread(new Runnable() { + + @Override + public void run() { + try{ + int count=0; + Message message; + while((message = outQueue.getQueue().take()) != null){ + if(!(message instanceof EndOfStreamMessage)){ + logger.info(count+" - "+message.toString()); + } + else{ + break; + } + count++; + } + } + catch (Exception e) { + e.printStackTrace(); + } + } + }); + + tf.start(); + t.start(); + tp.start(); + + tf.join(); + t.join(); + tp.join(); + } + + /** + * Test method for {@link ch.psi.fda.core.manipulator.Manipulator#run()}. + * @throws InterruptedException + */ + @Test + public void testRunMultipleParameter() throws InterruptedException { + DataMessageMetadata dmm = new DataMessageMetadata(); + dmm.getComponents().add(new ComponentMetadata("myid")); + dmm.getComponents().add(new ComponentMetadata("myid2")); + DataQueue inQueue = new DataQueue(new LinkedBlockingQueue(), dmm); + + DataMessage m = new DataMessage(); + m.getData().add(10d); + m.getData().add(0.2d); + inQueue.getQueue().add(m); + inQueue.getQueue().add(new EndOfStreamMessage()); + + String id="cid"; + String script = "import math\ndef process(o ,c):\n return math.cos(c) + math.sin(o)"; + List mapping = new ArrayList(); + mapping.add(new JythonParameterMappingID("o", "myid")); + mapping.add(new JythonParameterMappingID("c", "myid2")); + JythonManipulation manipulation = new JythonManipulation(id, script, mapping); + + List manipulations = new ArrayList(); + manipulations.add(manipulation); + Manipulator manipulator = new Manipulator(inQueue, manipulations); + + // Check whether output queue message structur complies to expected one + DataMessageMetadata outMeta = manipulator.getOutQueue().getDataMessageMetadata(); + + // Test whether only the expected components are within the outgoing queue + if(outMeta.getComponents().size()!=3){ + fail("There are more than the expected components in the outgoing message"); + } + + // Test whether the id of the first component matches the expected id + if(!outMeta.getComponents().get(0).getId().equals("myid")){ + fail("Id of the first component does not match the expected id 'myid'"); + } + + if(!outMeta.getComponents().get(1).getId().equals("myid2")){ + fail("Id of the first component does not match the expected id 'myid'"); + } + + // Test whether the id of the second component (which was computed) matches the expected id + if(!outMeta.getComponents().get(2).getId().equals("cid")){ + fail("Id of the second component does not match the expected id 'cid'"); + } + + + manipulator.run(); + + Message message = manipulator.getOutQueue().getQueue().take(); + while(!(message instanceof EndOfStreamMessage)){ + + logger.info(message.toString()); + + if(message instanceof DataMessage){ + DataMessage dm = (DataMessage) message; + dm.getData().get(0); + double res = ((Double)dm.getData().get(2)) - (Math.cos(((Double)dm.getData().get(1)))+Math.sin(((Double)dm.getData().get(0)))); + if( Math.abs(res) > 0.000000001){ + fail("Calculation failed"); + } + } + + + message = manipulator.getOutQueue().getQueue().take(); + } + + logger.info(""+(Math.cos(0.2)+Math.sin(10))); + + } + + + /** + * Test method for {@link ch.psi.fda.core.manipulator.Manipulator#run()}. + * @throws InterruptedException + * @throws CAException + */ + @Test + public void testRunMultipleParameterAndChannel() throws InterruptedException, CAException { + + String testChannel = "MTEST-PC-X10:AO"; + Double setValue = 12.22; + + ChannelBean cbean = ChannelBeanFactory.getFactory().createChannelBean(Double.class, testChannel, false); + + DataMessageMetadata dmm = new DataMessageMetadata(); + dmm.getComponents().add(new ComponentMetadata("myid")); + dmm.getComponents().add(new ComponentMetadata("myid2")); + DataQueue inQueue = new DataQueue(new LinkedBlockingQueue(), dmm); + + DataMessage m = new DataMessage(); + m.getData().add(10d); + m.getData().add(0.2d); + inQueue.getQueue().add(m); + inQueue.getQueue().add(new EndOfStreamMessage()); + + String id="cid"; + String script = "import math\ndef process(o ,c,d):\n d.setValue("+setValue+")\n print d.getValue()\n return math.cos(c) + math.sin(o)"; + List mapping = new ArrayList(); + mapping.add(new JythonParameterMappingID("o", "myid")); + mapping.add(new JythonParameterMappingID("c", "myid2")); + mapping.add(new JythonParameterMappingChannel("d", testChannel, Double.class)); + JythonManipulation manipulation = new JythonManipulation(id, script, mapping); + + List manipulations = new ArrayList(); + manipulations.add(manipulation); + Manipulator manipulator = new Manipulator(inQueue, manipulations); + + // Check whether output queue message structur complies to expected one + DataMessageMetadata outMeta = manipulator.getOutQueue().getDataMessageMetadata(); + + // Test whether only the expected components are within the outgoing queue + if(outMeta.getComponents().size()!=3){ + fail("There are more than the expected components in the outgoing message"); + } + + // Test whether the id of the first component matches the expected id + if(!outMeta.getComponents().get(0).getId().equals("myid")){ + fail("Id of the first component does not match the expected id 'myid'"); + } + + if(!outMeta.getComponents().get(1).getId().equals("myid2")){ + fail("Id of the first component does not match the expected id 'myid'"); + } + + // Test whether the id of the second component (which was computed) matches the expected id + if(!outMeta.getComponents().get(2).getId().equals("cid")){ + fail("Id of the second component does not match the expected id 'cid'"); + } + + // Change something different on the channel than the value that will be set in the manipulator script + cbean.setValue(setValue+1); + + manipulator.run(); + + Message message = manipulator.getOutQueue().getQueue().take(); + while(!(message instanceof EndOfStreamMessage)){ + + logger.info(message.toString()); + + if(message instanceof DataMessage){ + DataMessage dm = (DataMessage) message; + dm.getData().get(0); + double res = ((Double)dm.getData().get(2)) - (Math.cos(((Double)dm.getData().get(1)))+Math.sin(((Double)dm.getData().get(0)))); + if( Math.abs(res) > 0.000000001){ + fail("Calculation failed"); + } + } + + + message = manipulator.getOutQueue().getQueue().take(); + } + + logger.info(""+(Math.cos(0.2)+Math.sin(10))); + + // Check whether the channel was set correctly by the manipulator script + if(Math.abs(cbean.getValue()-setValue)>0.00000001){ + fail("Channel was not set correctly in the manipulator script"); + } + + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/sensors/ChannelAccessDoubleArraySensorTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/sensors/ChannelAccessDoubleArraySensorTest.java new file mode 100644 index 0000000..eed852c --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/sensors/ChannelAccessDoubleArraySensorTest.java @@ -0,0 +1,86 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.sensors; + +import static org.junit.Assert.*; + +import java.util.logging.Logger; + +import gov.aps.jca.CAException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.TestChannels; +import ch.psi.fda.core.sensors.ChannelAccessDoubleArraySensor; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * Test class for the ScalarDoubleSensorChannelAccess class. + * @author ebner + * + */ +public class ChannelAccessDoubleArraySensorTest { + + // Get Logger + private static Logger logger = Logger.getLogger(ChannelAccessDoubleArraySensorTest.class.getName()); + + private static final String channelName = TestChannels.DOUBLE_WAVEFORM; + private static final int numberOfPoints = 10; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.sensors.ChannelAccessDoubleSensor#read()}. + * @throws CAException + */ + @Test + public void testRead() throws CAException, InterruptedException { + ChannelAccessDoubleArraySensor sensor = new ChannelAccessDoubleArraySensor("id0", channelName, numberOfPoints); + ChannelBean channel = ChannelBeanFactory.getFactory().createChannelBean(double[].class, channelName, false); + + // Prepare sensor channel + double[] setValue = new double[] { 0.1,0.2,0.3,4,5,6,77,88,99,10.2}; + channel.setValue(setValue); + + // Get sensor readout value + double[] value = (double[]) sensor.read(); + for(int i=0;i0.0000001){ // The precision of the channel is 6 + fail("Sensor readout value does not match actual value"); + } + } + } +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/sensors/ChannelAccessDoubleSensorTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/sensors/ChannelAccessDoubleSensorTest.java new file mode 100644 index 0000000..5e71912 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/sensors/ChannelAccessDoubleSensorTest.java @@ -0,0 +1,84 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.sensors; + +import static org.junit.Assert.*; + +import java.util.logging.Logger; + +import gov.aps.jca.CAException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.TestChannels; +import ch.psi.fda.core.sensors.ChannelAccessDoubleSensor; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * Test class for the ScalarDoubleSensorChannelAccess class. + * @author ebner + * + */ +public class ChannelAccessDoubleSensorTest { + + // Get Logger + private static Logger logger = Logger.getLogger(ChannelAccessDoubleSensorTest.class.getName()); + + private static final String channelName = TestChannels.ANALOG_OUT; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.sensors.ChannelAccessDoubleSensor#read()}. + * @throws CAException + */ + @Test + public void testRead() throws CAException, InterruptedException { + ChannelAccessDoubleSensor sensor = new ChannelAccessDoubleSensor("id0", channelName); + ChannelBean channel = ChannelBeanFactory.getFactory().createChannelBean(Double.class, channelName, false); + + // Prepare sensor channel + Double setValue = 0.1d; + channel.setValue(setValue); + + // Get sensor readout value + Double value = (Double) sensor.read(); + logger.finest("Sensor value: "+value); + if(!value.equals(setValue)){ + fail("Sensor readout value does not match actual value"); + } + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/sensors/ChannelAccessStringSensorTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/sensors/ChannelAccessStringSensorTest.java new file mode 100644 index 0000000..5ded394 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/sensors/ChannelAccessStringSensorTest.java @@ -0,0 +1,77 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.sensors; + +import static org.junit.Assert.*; + +import java.util.logging.Logger; + +import gov.aps.jca.CAException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.TestChannels; + +/** + * Test class for the ScalarDoubleSensorChannelAccess class. + * @author ebner + * + */ +public class ChannelAccessStringSensorTest { + + // Get Logger + private static Logger logger = Logger.getLogger(ChannelAccessStringSensorTest.class.getName()); + + private static final String channelName = TestChannels.ANALOG_OUT+".NAME"; + private static final String actualValue = TestChannels.ANALOG_OUT; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.sensors.ChannelAccessDoubleSensor#read()}. + * @throws CAException + */ + @Test + public void testRead() throws CAException, InterruptedException { + ChannelAccessStringSensor sensor = new ChannelAccessStringSensor("id0", channelName); + + // Get sensor readout value + String value = (String) sensor.read(); + logger.info("Sensor value: "+value); + if(!value.equals(actualValue)){ + fail("Sensor readout "+value+" value does not match actual value "+actualValue); + } + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/core/sensors/ComplexSensorTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/core/sensors/ComplexSensorTest.java new file mode 100644 index 0000000..41eb035 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/core/sensors/ComplexSensorTest.java @@ -0,0 +1,133 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.core.sensors; + +import static org.junit.Assert.*; + +import java.util.HashMap; +import java.util.logging.Logger; + +import gov.aps.jca.CAException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.TestChannels; +import ch.psi.fda.core.Action; +import ch.psi.fda.core.sensors.ChannelAccessDoubleSensor; +import ch.psi.fda.core.sensors.ComplexSensor; +import ch.psi.jcae.ChannelBean; +import ch.psi.jcae.ChannelBeanFactory; + +/** + * Test class for the ScalarDoubleSensorChannelAccess class. + * @author ebner + * + */ +public class ComplexSensorTest { + + // Get Logger + private static Logger logger = Logger.getLogger(ComplexSensorTest.class.getName()); + + private static final String channelName = TestChannels.ANALOG_OUT; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.sensors.ChannelAccessDoubleSensor#read()}. + * @throws CAException + */ + @Test + public void testRead() throws CAException, InterruptedException { + + final HashMap timestamps = new HashMap(); + + ChannelAccessDoubleSensor s1 = new ChannelAccessDoubleSensor("id0", channelName); + ComplexSensor sensor = new ComplexSensor("id2", s1); + + // Add pre action + sensor.getPreActions().add(new Action() { + + @Override + public void execute() { + logger.info("PreAction"); + timestamps.put("pre", System.currentTimeMillis()); + } + + @Override + public void destroy() { + } + + @Override + public void abort() { + } + }); + + // Add post action + sensor.getPostActions().add(new Action() { + + @Override + public void execute() { + logger.info("PostAction"); + timestamps.put("post", System.currentTimeMillis()); + } + + @Override + public void destroy() { + } + + @Override + public void abort() { + } + }); + + // Create channel bean for test sensor channel + ChannelBean channel = ChannelBeanFactory.getFactory().createChannelBean(Double.class, channelName, false); + + // Prepare sensor channel + Double setValue = 0.1d; + channel.setValue(setValue); + + // Get sensor readout value + Double value = (Double) sensor.read(); + + logger.finest("Sensor value: "+value); + if(!value.equals(setValue)){ + fail("Sensor readout value does not match actual value"); + } + + if(timestamps.get("post"). + * + */ + +package ch.psi.fda.core.sensors; + + +import java.util.logging.Logger; + +import gov.aps.jca.CAException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.core.sensors.MillisecondTimestampSensor; + +/** + * Test class for MillisecondsTimestampSensor + * @author ebner + * + */ +public class MillisecondTimestampSensorTest { + + // Get Logger + private static Logger logger = Logger.getLogger(MillisecondTimestampSensorTest.class.getName()); + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.core.sensors.ChannelAccessDoubleSensor#read()}. + * @throws CAException + */ + @Test + public void testRead() throws CAException { + MillisecondTimestampSensor sensor = new MillisecondTimestampSensor("id0"); + + // Get sensor readout value + Double value = (Double) sensor.read(); + logger.finest("Sensor value: "+value); + + } +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/deserializer/DataDeserializerMDATest.java b/ch.psi.fda/src/test/java/ch/psi/fda/deserializer/DataDeserializerMDATest.java new file mode 100644 index 0000000..ebfebe8 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/deserializer/DataDeserializerMDATest.java @@ -0,0 +1,127 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.deserializer; + +import java.io.File; +import java.net.URI; +import java.net.URL; +import java.util.concurrent.BlockingQueue; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.ControlMessage; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; + +/** + * @author ebner + * + * For testing whether the created MDA file is correct use following + * program on a SLS SL5 beamline console (or slslc05/06) + */ +public class DataDeserializerMDATest { + + // Get Logger + private static Logger logger = Logger.getLogger(DataDeserializerMDATest.class.getName()); + + private DataDeserializerMDA deserializer; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + URL url = this.getClass().getClassLoader().getResource("testdata/mda/mdadata7.mda"); + deserializer = new DataDeserializerMDA(new File(new URI(url.toString()))); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.deserializer.DataDeserializerTXT#run()}. + * @throws InterruptedException + */ + @Test + public void testRun() throws InterruptedException { + + Thread t = new Thread(new Runnable() { + @Override + public void run() { + try { + BlockingQueue q = deserializer.getQueue().getQueue(); + + while(true){ + Message m = q.take(); + if(m instanceof DataMessage){ + DataMessage x = (DataMessage) m; + logger.info( x.toString() ); + } + else if(m instanceof ControlMessage){ + if(m instanceof EndOfStreamMessage){ + break; + } + logger.info("---- "+m.toString()+" ----"); + } + } + + StringBuilder b = new StringBuilder(); + b.append("["); + StringBuilder b1 = new StringBuilder(); + b1.append("["); + for(ComponentMetadata cm : deserializer.getQueue().getDataMessageMetadata().getComponents()){ + b.append(" "); + b.append(cm.getId()); + b1.append(" "); + b1.append(cm.getDimension()); + } + b.append(" ]"); + b1.append(" ]"); + + logger.info("Metadata "+b.toString()); + logger.info("Metadata "+b1.toString()); + } catch (InterruptedException e) { + logger.log(Level.SEVERE, "An Exception occured while reading data from the data queue", e); + } + } + }); + + Thread tt = new Thread(deserializer); + + tt.start(); + t.start(); + + tt.join(); + t.join(); + + + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/deserializer/DataDeserializerTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/deserializer/DataDeserializerTest.java new file mode 100644 index 0000000..dd81ba3 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/deserializer/DataDeserializerTest.java @@ -0,0 +1,107 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.deserializer; + +import java.io.File; +import java.net.URI; +import java.net.URL; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.core.messages.ControlMessage; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; +import ch.psi.fda.deserializer.DataDeserializer; +import ch.psi.fda.deserializer.DataDeserializerTXT; + +/** + * @author ebner + * + */ +public class DataDeserializerTest { + + // Get Logger + private static Logger logger = Logger.getLogger(DataDeserializerTest.class.getName()); + + private DataDeserializer deserializer; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + URL url = this.getClass().getClassLoader().getResource("testdata/text/textdata2.txt"); + deserializer = new DataDeserializerTXT(new File(new URI(url.toString()))); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.deserializer.DataDeserializerTXT#run()}. + * @throws InterruptedException + */ + @Test + public void testRun() throws InterruptedException { + + Thread t = new Thread(new Runnable() { + @Override + public void run() { + try { + while(true){ + Message m = deserializer.getQueue().getQueue().take(); + if(m instanceof DataMessage){ + DataMessage x = (DataMessage) m; + logger.info( x.toString() ); + } + else if(m instanceof ControlMessage){ + if(m instanceof EndOfStreamMessage){ + break; + } + logger.info("---- "+m.toString()+" ----"); + } + } + } catch (InterruptedException e) { + logger.log(Level.SEVERE, "An Exception occured while reading data from the data queue", e); + } + } + }); + + Thread tt = new Thread(deserializer); + + tt.start(); + t.start(); + + tt.join(); + t.join(); + + + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/install/ApplicationConfiguratorTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/install/ApplicationConfiguratorTest.java new file mode 100644 index 0000000..88b7e82 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/install/ApplicationConfiguratorTest.java @@ -0,0 +1,57 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.install; + +import java.io.IOException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * @author ebner + * + */ +public class ApplicationConfiguratorTest { + + private ApplicationConfigurator configurator; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + System.setProperty(ApplicationConfigurator.FDA_HOME_ARGUMENT, "target/tmp/home"); + configurator = new ApplicationConfigurator(); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + @Test + public void testInitializeApplication() throws IOException{ + configurator.initializeApplication(); + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/model/ModelManagerTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/model/ModelManagerTest.java new file mode 100644 index 0000000..c691f92 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/model/ModelManagerTest.java @@ -0,0 +1,113 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.model; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.logging.Logger; + +import javax.xml.bind.JAXBException; +import javax.xml.bind.MarshalException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.xml.sax.SAXException; + +import ch.psi.fda.model.ModelManager; +import ch.psi.fda.model.v1.Configuration; +import ch.psi.fda.model.v1.LinePlot; +import ch.psi.fda.model.v1.Scan; + +/** + * @author ebner + * + */ +public class ModelManagerTest { + + private static final String tmpDirectory = "target/tmp"; + + // Get Logger + private static Logger logger = Logger.getLogger(ModelManagerTest.class.getName()); + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + new File(tmpDirectory).mkdirs(); // Create temp directory + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.model.ModelManager#unmarshall(java.io.File)}. + * @throws SAXException + * @throws JAXBException + * @throws URISyntaxException + */ + @Test + public void testUnmarshall() throws JAXBException, SAXException, URISyntaxException { + URL url = this.getClass().getClassLoader().getResource("home/scans/templates/scan1d.xml"); + Configuration c = ModelManager.unmarshall(new File(new URI(url.toString()))); + logger.info(""+c.getData().getFormat()); + } + + /** + * Test method for {@link ch.psi.fda.model.ModelManager#marshall(ch.psi.fda.model.v1.Configuration, java.io.File)}. + * @throws SAXException + * @throws JAXBException + */ + @Test(expected=MarshalException.class) + public void testMarshallFail() throws JAXBException, SAXException { + + Configuration c = new Configuration(); + c.setDescription("My Description"); + Scan s = new Scan(); + c.setScan(s); + + // Add component that is not match to xsd + LinePlot v = new LinePlot(); + c.getVisualization().add(v); + + ModelManager.marshall(c, new File(tmpDirectory+"/scan.xml")); + + } + + @Test + public void testMarshall() throws JAXBException, SAXException { + + Configuration c = new Configuration(); + c.setDescription("My Description"); + Scan s = new Scan(); + c.setScan(s); + + ModelManager.marshall(c, new File(tmpDirectory+"/scan.xml")); + + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/serializer/DataSerializerTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/serializer/DataSerializerTest.java new file mode 100644 index 0000000..d6bb89a --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/serializer/DataSerializerTest.java @@ -0,0 +1,235 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.serializer; + +import java.io.File; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.StreamDelimiterMessage; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; +import ch.psi.fda.serializer.DataSerializer; +import ch.psi.fda.serializer.DataSerializerMAT; +import ch.psi.fda.serializer.DataSerializerMAT2D; +import ch.psi.fda.serializer.DataSerializerTXT; +import ch.psi.fda.serializer.DataSerializerTXT2D; +import ch.psi.fda.serializer.DataSerializerTXTSplit; + +/** + * @author ebner + * + */ +public class DataSerializerTest { + + private static final String tmpDirectory = "target/tmp"; + + + private DataQueue queue; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + new File(tmpDirectory).mkdirs(); + BlockingQueue q3 = new LinkedBlockingQueue(); + DataMessageMetadata m3 = new DataMessageMetadata(); + + this.queue = new DataQueue(q3, m3); + + } + + /** + * Generate 1D data + * @throws InterruptedException + */ + private void generate1DData() throws InterruptedException{ + + queue.getDataMessageMetadata().getComponents().add(new ComponentMetadata("id0", 0)); + queue.getDataMessageMetadata().getComponents().add(new ComponentMetadata("id1", 0)); + queue.getDataMessageMetadata().getComponents().add(new ComponentMetadata("id2", 0)); + + // Dimension + DataMessage m = new DataMessage(); + m.getData().add(0.000000000000000001); + m.getData().add(0.1); + m.getData().add(1d); // have this value as double + queue.getQueue().put(m); + + m = new DataMessage(); + m.getData().add(0.02); + m.getData().add(0.2); + m.getData().add(2d); // have this value as double + queue.getQueue().put(m); + queue.getQueue().put(new EndOfStreamMessage()); + } + + /** + * Generate 2D test data + * @throws InterruptedException + */ + private void generate2DData() throws InterruptedException{ + + queue.getDataMessageMetadata().getComponents().add(new ComponentMetadata("id0", 1)); + queue.getDataMessageMetadata().getComponents().add(new ComponentMetadata("id1", 0)); + queue.getDataMessageMetadata().getComponents().add(new ComponentMetadata("id2", 0)); + + for(double i=0;i<5;i++){ + for(double t=0.1; t<10; t=t+0.1){ + // Dimension + DataMessage m = new DataMessage(); + m.getData().add(i); + m.getData().add(t); + m.getData().add(Math.log(t)); // have this value as double + queue.getQueue().put(m); + } + queue.getQueue().put(new StreamDelimiterMessage(0)); + } + queue.getQueue().put(new StreamDelimiterMessage(1)); + + + queue.getQueue().put(new EndOfStreamMessage()); + } + + /** + * Generate 3d test data + * @throws InterruptedException + */ + private void generate3DData() throws InterruptedException{ + + queue.getDataMessageMetadata().getComponents().add(new ComponentMetadata("id0", 2)); + queue.getDataMessageMetadata().getComponents().add(new ComponentMetadata("id1", 1)); + queue.getDataMessageMetadata().getComponents().add(new ComponentMetadata("id2", 0)); + queue.getDataMessageMetadata().getComponents().add(new ComponentMetadata("id3", 0)); + + for(double z=30;z<36;z++){ + for(double i=0;i<6;i++){ + for(double t=0.1; t<1.1; t=t+0.1){ + // Dimension + DataMessage m = new DataMessage(); + m.getData().add(z); + m.getData().add(i); + m.getData().add(t); + m.getData().add(Math.log(t)); // have this value as double + queue.getQueue().put(m); + } + queue.getQueue().put(new StreamDelimiterMessage(0)); + } + queue.getQueue().put(new StreamDelimiterMessage(1)); + } + queue.getQueue().put(new StreamDelimiterMessage(2)); + + + queue.getQueue().put(new EndOfStreamMessage()); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.serializer.DataSerializerTXT#run()}. + * @throws InterruptedException + */ + @Test + public void testRunTXT() throws InterruptedException { + generate1DData(); + DataSerializer serializer = new DataSerializerTXT(queue, new File(tmpDirectory+"/test.txt"), true); + serializer.run(); + } + + /** + * Test method for {@link ch.psi.fda.serializer.DataSerializerTXT#run()}. + * @throws InterruptedException + */ + @Test + public void testRunMAT() throws InterruptedException { + generate1DData(); + DataSerializer serializer = new DataSerializerMAT(queue, new File(tmpDirectory+"/test.mat")); + serializer.run(); + } + + /** + * Test method for {@link ch.psi.fda.serializer.DataSerializerTXT#run()}. + * @throws InterruptedException + */ + @Test + public void testRunMAT2D() throws InterruptedException { + generate2DData(); + DataSerializer serializer = new DataSerializerMAT2D(queue, new File(tmpDirectory+"/test-2d.mat")); + serializer.run(); + } + + /** + * Test method for {@link ch.psi.fda.serializer.DataSerializerTXT#run()}. + * @throws InterruptedException + */ + @Test + public void testRunTXT2D() throws InterruptedException { + generate2DData(); + DataSerializer serializer = new DataSerializerTXT2D(queue, new File(tmpDirectory+"/test-2d.txt")); + serializer.run(); + } + + /** + * Test method for {@link ch.psi.fda.serializer.DataSerializerTXT#run()}. + * @throws InterruptedException + */ + @Test + public void testRunSplitTXT() throws InterruptedException { + generate2DData(); + DataSerializer serializer = new DataSerializerTXTSplit(queue, new File(tmpDirectory+"/test-2d-split.txt")); + serializer.run(); + } + + /** + * Test method for {@link ch.psi.fda.serializer.DataSerializerMDA#run()}. + * @throws InterruptedException + */ + @Test + public void testRun2D() throws InterruptedException { + generate2DData(); + DataSerializer serializer = new DataSerializerMDA(queue, new File(tmpDirectory+"/test-2d.mda")); + serializer.run(); + } + + /** + * Test method for {@link ch.psi.fda.serializer.DataSerializerMDA#run()}. + * @throws InterruptedException + */ + @Test + public void testRun3D() throws InterruptedException { + generate3DData(); + DataSerializer serializer = new DataSerializerMDA(queue, new File(tmpDirectory+"/test-3d.mda")); + serializer.run(); + } +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/vis/VisualizationEngineTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/vis/VisualizationEngineTest.java new file mode 100644 index 0000000..4c889b1 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/vis/VisualizationEngineTest.java @@ -0,0 +1,124 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.vis; + +import java.io.File; +import java.net.URI; +import java.net.URL; + +import javax.xml.bind.JAXBException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.xml.sax.SAXException; + +import ch.psi.fda.vis.VisualizationEngine; + +/** + * Testcase for visualization engine + * @author ebner + * + */ +public class VisualizationEngineTest { + + private VisualizationEngine engine; + private String configurationFile; + private String dataFile; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + URL url = this.getClass().getClassLoader().getResource("testdata/scan/scan1d.xml"); + configurationFile = new File(new URI(url.toString())).getAbsolutePath(); + + url = this.getClass().getClassLoader().getResource("testdata/scan/scan1d_0000.txt"); + dataFile = new File(new URI(url.toString())).getAbsolutePath(); + + engine = new VisualizationEngine(); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.vis.VisualizationEngine#visualize(java.io.File, java.io.File)}. + * Test whether a null data parameter is handled correctly + * @throws InterruptedException + * @throws SAXException + * @throws JAXBException + */ + @Test(expected=RuntimeException.class) + public void testVisualizeFileFileDataNull() throws InterruptedException { + engine.visualize(new File(configurationFile), null); + } + + /** + * Test method for {@link ch.psi.fda.vis.VisualizationEngine#visualize(java.io.File, java.io.File)}. + * Test whether a non existing data file is handled correctly + * @throws InterruptedException + * @throws SAXException + * @throws JAXBException + */ + @Test(expected=RuntimeException.class) + public void testVisualizeFileFileDataNoExist() throws InterruptedException { + engine.visualize(new File(configurationFile), new File("")); + } + + /** + * Test method for {@link ch.psi.fda.vis.VisualizationEngine#visualize(java.io.File, java.io.File)}. + * Check whether null configuration file is handled correctly + * @throws InterruptedException + * @throws SAXException + * @throws JAXBException + */ + @Test(expected=RuntimeException.class) + public void testVisualizeFileFileConfigNull() throws InterruptedException { + engine.visualize(new File(""), new File(dataFile)); + } + + /** + * Test method for {@link ch.psi.fda.vis.VisualizationEngine#visualize(java.io.File, java.io.File)}. + * Test correct visualization + * @throws InterruptedException + * @throws SAXException + * @throws JAXBException + */ + @Test + public void testVisualizeFileFile() throws InterruptedException { + engine.visualize(new File(configurationFile), new File(dataFile)); + } + + /** + * Test whether main can automatically determine the configuration file for a given data file + * Test method for {@link ch.psi.fda.vis.VisualizationEngine#main(String[])} + */ + @Test + public void testMain(){ + // ATTENTION - This test will never fail because possible exceptions are caught in the main method !!!! + VisualizationEngine.main(new String[]{dataFile}); + } +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/visualizer/DataVisualizerTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/visualizer/DataVisualizerTest.java new file mode 100644 index 0000000..f75b104 --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/visualizer/DataVisualizerTest.java @@ -0,0 +1,303 @@ +/** + * + * Copyright 2010 Paul Scherrer Institute. All rights reserved. + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + * + */ + +package ch.psi.fda.visualizer; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import ch.psi.fda.core.messages.ComponentMetadata; +import ch.psi.fda.core.messages.DataMessage; +import ch.psi.fda.core.messages.DataMessageMetadata; +import ch.psi.fda.core.messages.DataQueue; +import ch.psi.fda.core.messages.StreamDelimiterMessage; +import ch.psi.fda.core.messages.EndOfStreamMessage; +import ch.psi.fda.core.messages.Message; +import ch.psi.fda.model.v1.MatrixPlot; +import ch.psi.fda.model.v1.PseudoPositioner; +import ch.psi.fda.model.v1.Visualization; +import ch.psi.fda.visualizer.Visualizer; + +/** + * All test cases in this test class are meant to be executed manually + * Remove @Ignore in front of the test function to be able to run it. + * + * @author ebner + * + */ +public class DataVisualizerTest { + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.visualizer.Visualizer#run()}. + * @throws InterruptedException + */ + @Ignore + @Test + public void testRun() throws InterruptedException { + final BlockingQueue q = new LinkedBlockingQueue(); + DataMessageMetadata dm = new DataMessageMetadata(); + dm.getComponents().add(new ComponentMetadata("id1", 0)); + dm.getComponents().add(new ComponentMetadata("id2", 0)); + dm.getComponents().add(new ComponentMetadata("id3", 0)); + DataQueue queue = new DataQueue(q,dm); + + // Create visualization + List vlist = new ArrayList(); + ch.psi.fda.model.v1.LinePlot p = new ch.psi.fda.model.v1.LinePlot(); + p.setX("id1"); + p.getY().add("id2"); + vlist.add(p); + p = new ch.psi.fda.model.v1.LinePlot(); + p.setX("id1"); + p.getY().add("id3"); + vlist.add(p); + + // Create visualizer + Visualizer visualizer = new Visualizer(queue, vlist); + + JFrame f = new JFrame(); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.setSize(600, 400); + JPanel pan = new JPanel(); + for(JPanel pp: visualizer.getPlotPanels()){ + pan.add(pp); + } + f.add(pan); + f.setVisible(true); + + + // Thread creating data + Thread t = new Thread(new Runnable() { + + @Override + public void run() { + DataMessage m; + for(double t=0;t<4;t++){ + for(double i =0;i<100;i=i+0.1){ + m = new DataMessage(); + m.getData().add(i); + m.getData().add(t+Math.sin(i)); + m.getData().add(t+Math.cos(i)); + q.add(m); + try { + Thread.sleep(10); + } catch (InterruptedException e) { + throw new RuntimeException("Sleep interrupted",e); + } + } + q.add(new StreamDelimiterMessage(0)); + } + + q.add(new EndOfStreamMessage()); + + } + }); + // Start data thread + t.start(); + + + visualizer.visualize(); + + t.join(); + + Thread.sleep(10000); + } + + @Ignore + @Test + public void testRunArray() throws InterruptedException { + final BlockingQueue q = new LinkedBlockingQueue(); + DataMessageMetadata dm = new DataMessageMetadata(); + dm.getComponents().add(new ComponentMetadata("id1", 0)); + DataQueue queue = new DataQueue(q,dm); + + // Create visualization + List vlist = new ArrayList(); + ch.psi.fda.model.v1.LinePlotArray p = new ch.psi.fda.model.v1.LinePlotArray(); + p.getY().add("id1"); + p.setMaxSeries(10); + vlist.add(p); + + // Create visualizer + Visualizer visualizer = new Visualizer(queue, vlist); + + JFrame f = new JFrame(); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.setSize(600, 400); + JPanel pan = new JPanel(); + for(JPanel pp: visualizer.getPlotPanels()){ + pan.add(pp); + } + f.add(pan); + f.setVisible(true); + + + // Thread creating data + Thread t = new Thread(new Runnable() { + + @Override + public void run() { + DataMessage m; + int npoints = 10000; + for(double t=0;t<10;t++){ + double[] values = new double[npoints]; + + for(int i=0;i q = new LinkedBlockingQueue(); + DataMessageMetadata dm = new DataMessageMetadata(); + dm.getComponents().add(new ComponentMetadata("id1", 1)); + dm.getComponents().add(new ComponentMetadata("id2", 0)); + dm.getComponents().add(new ComponentMetadata("id3", 0)); + DataQueue queue = new DataQueue(q,dm); + + List vlist = new ArrayList(); + ch.psi.fda.model.v1.LinePlot p = new ch.psi.fda.model.v1.LinePlot(); + p.setX("id2"); + p.getY().add("id3"); + vlist.add(p); + +// 0d,4d,5,0d,100d,1001 + + PseudoPositioner pos = new PseudoPositioner(); + pos.setId("id1"); + pos.setCounts(5); + + PseudoPositioner pos1 = new PseudoPositioner(); + pos1.setId("id2"); + pos1.setCounts(1000); + + MatrixPlot mp = new MatrixPlot(); + mp.setX(pos); + mp.setY(pos1); + mp.setZ("id3"); + vlist.add(mp); + + // Create visualizer + Visualizer visualizer = new Visualizer(queue, vlist); + + JFrame f = new JFrame(); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.setSize(600, 400); +// f.add(plot.getPlotPanel()); + JPanel pan = new JPanel(); + for(JPanel pp: visualizer.getPlotPanels()){ + pan.add(pp); + } + f.add(pan); + f.setVisible(true); + + + // Thread creating data + Thread t = new Thread(new Runnable() { + + @Override + public void run() { + DataMessage m; + for(double t=1;t<=5;t++){ + for(double i=1;i<=1000;i++){ + m = new DataMessage(); + m.getData().add(t); + m.getData().add(i); + m.getData().add(t+Math.cos(i)); + q.add(m); + try { + Thread.sleep(10); + } catch (InterruptedException e) { + throw new RuntimeException("Sleep interrupted",e); + } + } + q.add(new StreamDelimiterMessage(0)); + } + + q.add(new EndOfStreamMessage()); + + } + }); + // Start data thread + t.start(); + + + visualizer.visualize(); + + t.join(); + + Thread.sleep(10000); + } + +} diff --git a/ch.psi.fda/src/test/resources/home/config/cdump.properties b/ch.psi.fda/src/test/resources/home/config/cdump.properties new file mode 100644 index 0000000..e69de29 diff --git a/ch.psi.fda/src/test/resources/home/config/fda.properties b/ch.psi.fda/src/test/resources/home/config/fda.properties new file mode 100644 index 0000000..a4a79fe --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/config/fda.properties @@ -0,0 +1,14 @@ +# Serialization properties +ch.psi.fda.aq.data.filePrefix=${yyyy_MM}/${yyyyMMdd}/${yyyyMMddHHmmss}_${name}/${yyyyMMddHHmm}_ + +# OTF scan related configuration +ch.psi.fda.aq.otf.channelPrefix=MTEST-HW3-OTFX +ch.psi.fda.aq.otf.nfsServer=slsyoke1.psi.ch +ch.psi.fda.aq.otf.nfsShare=/usr/local/nfsshare +ch.psi.fda.aq.otf.smbShare=smb://:@sdc.psi.ch/otftemp/ + +ch.psi.fda.aq.otf.useCrlogic=true +ch.psi.fda.aq.otf.crlogicPrefix=MTEST-HW3-CRL +ch.psi.fda.aq.otf.scalerPrefix=MTEST-HW3:JS + +ch.psi.fda.aq.notification.recipients=simon.ebner@psi.ch \ No newline at end of file diff --git a/ch.psi.fda/src/test/resources/home/config/jcae.properties b/ch.psi.fda/src/test/resources/home/config/jcae.properties new file mode 100644 index 0000000..aed63f1 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/config/jcae.properties @@ -0,0 +1,6 @@ +ch.psi.jcae.ContextFactory.addressList=129.129.130.255 129.129.130.37 129.129.145.26 129.129.130.77 +ch.psi.jcae.ChannelFactory.timeout=2000 +ch.psi.jcae.ChannelFactory.retries=4 +ch.psi.jcae.ChannelBeanFactory.timeout=10000 +ch.psi.jcae.ChannelBeanFactory.waitTimeout=3600000 +ch.psi.jcae.ChannelBeanFactory.retries=4 \ No newline at end of file diff --git a/ch.psi.fda/src/test/resources/home/config/logging.properties b/ch.psi.fda/src/test/resources/home/config/logging.properties new file mode 100644 index 0000000..f1781f1 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/config/logging.properties @@ -0,0 +1,32 @@ +# Specify the handlers to create in the root logger +##handlers = java.util.logging.ConsoleHandler, java.util.logging.FileHandler +handlers = java.util.logging.ConsoleHandler + +# Set the default logging level for the root logger +.level=INFO + +# Set the default logging level for new ConsoleHandler instances +java.util.logging.ConsoleHandler.level=ALL + +# Set the default formatter for new ConsoleHandler instances +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter + +# Set the default logging level for new FileHandler instances +##java.util.logging.FileHandler.level=ALL + +# Set the default formatter for new ConsoleHandler instances +##java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter + +# Naming of the output file: +##java.util.logging.FileHandler.pattern=/Users/ebner/Workspace/Eclipse/workspace-xasec/ch.psi.x10/resources/logs/fda-%u.%g.log + +# Limiting size of output file in bytes (10000kb): +##java.util.logging.FileHandler.limit=10000000 + +# Number of output files to cycle through, by appending an +# integer to the base file name: +##java.util.logging.FileHandler.count=10 + +# Set the default logging level for the logger named com.mycompany +ch.psi.fda.level=ALL +com.cosylab.epics.level=ALL diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/Co_EXAFS_GeD.xml b/ch.psi.fda/src/test/resources/home/scans/templates/Co_EXAFS_GeD.xml new file mode 100644 index 0000000..aa60d19 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/Co_EXAFS_GeD.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + 7.7 + 7.71 + 0.005 + + + + + 7.71 + 7.732 + 0.001 + + + + + 7.732 + 7.75 + 0.0005 + + + + + 7.75 + 7.8 + 0.001 + + + + + 7.8 + 7.9 + 0.0015 + + + + + 7.9 + 8.0 + 0.002 + + + + + 8.0 + 8.1 + 0.0025 + + + + + 8.1 + 8.2 + 0.003 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/GUI_testing_extMot_otf.xml b/ch.psi.fda/src/test/resources/home/scans/templates/GUI_testing_extMot_otf.xml new file mode 100644 index 0000000..024c994 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/GUI_testing_extMot_otf.xml @@ -0,0 +1,74 @@ + + + + + + + + + + 0.0 + 0.5 + 0.0020 + 0.05 + + + + + + + + + + + + + + + + + 10 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/StopGo_X10DA-DCM_Fl.xml b/ch.psi.fda/src/test/resources/home/scans/templates/StopGo_X10DA-DCM_Fl.xml new file mode 100644 index 0000000..6fc537b --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/StopGo_X10DA-DCM_Fl.xml @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + 8.97 + 9.000 + 0.0005 + + + + 9.000 + 9.170 + 0.001 + + + + 9.170 + 9.320 + 0.002 + + + + 9.320 + 9.690 + 0.003 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/TestXY__OTF_2D_v01_small-map.xml b/ch.psi.fda/src/test/resources/home/scans/templates/TestXY__OTF_2D_v01_small-map.xml new file mode 100644 index 0000000..84c99ad --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/TestXY__OTF_2D_v01_small-map.xml @@ -0,0 +1,31 @@ + + + + + + + -1.0 + 1.0 + 0.01 + 0.0050 + + + + + + + + + -1.0 + 1.0 + 0.25 + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d-crlogic.xml b/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d-crlogic.xml new file mode 100644 index 0000000..f81c8f7 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d-crlogic.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + 0.0 + 2.0 + 0.01 + 0.01 + + + + + + + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d-manipulation.xml b/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d-manipulation.xml new file mode 100644 index 0000000..71034de --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d-manipulation.xml @@ -0,0 +1,35 @@ + + + + + + + + 0.0 + 8.0 + 0.5 + 0.5 + + + + + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d-maxv.xml b/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d-maxv.xml new file mode 100644 index 0000000..ad49cc2 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d-maxv.xml @@ -0,0 +1,27 @@ + + + + + + + + 8.0 + 14.0 + 0.5 + 0.5 + + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d-prePostAction.xml b/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d-prePostAction.xml new file mode 100644 index 0000000..1dfed82 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d-prePostAction.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + 0.0 + 8.0 + 0.5 + 0.5 + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d-scrlogic.xml b/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d-scrlogic.xml new file mode 100644 index 0000000..d4ee9a9 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d-scrlogic.xml @@ -0,0 +1,30 @@ + + + + + + + + 0.0 + 2.0 + 0.01 + 0.01 + + + + + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d.xml b/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d.xml new file mode 100644 index 0000000..0eca1c6 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/cscan1d.xml @@ -0,0 +1,27 @@ + + + + + + + + 0.0 + 8.0 + 0.5 + 0.5 + + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/cscan2d-crlogic.xml b/ch.psi.fda/src/test/resources/home/scans/templates/cscan2d-crlogic.xml new file mode 100644 index 0000000..8770396 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/cscan2d-crlogic.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + 0.0 + 2.0 + 0.01 + 0.01 + + + + + + + + + + 3 + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/cscan2d-zigzag.xml b/ch.psi.fda/src/test/resources/home/scans/templates/cscan2d-zigzag.xml new file mode 100644 index 0000000..d1b1653 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/cscan2d-zigzag.xml @@ -0,0 +1,27 @@ + + + + + + + 0.0 + 8.0 + 0.5 + 0.5 + + + + + + + -1.0 + -2.0 + 0.1 + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/cscan2d.xml b/ch.psi.fda/src/test/resources/home/scans/templates/cscan2d.xml new file mode 100644 index 0000000..eb9d043 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/cscan2d.xml @@ -0,0 +1,29 @@ + + + + + + + 0.0 + 8.0 + 0.5 + 0.5 + + + + + + + -1.0 + -2.0 + 0.1 + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan0d-pseudo.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan0d-pseudo.xml new file mode 100644 index 0000000..54df343 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan0d-pseudo.xml @@ -0,0 +1,14 @@ + + + + + + + 100 + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan0d.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan0d.xml new file mode 100644 index 0000000..5b203ce --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan0d.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-array.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-array.xml new file mode 100644 index 0000000..b67a311 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-array.xml @@ -0,0 +1,25 @@ + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-arrayManipulation.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-arrayManipulation.xml new file mode 100644 index 0000000..574295a --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-arrayManipulation.xml @@ -0,0 +1,68 @@ + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-arrayManipulation2.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-arrayManipulation2.xml new file mode 100644 index 0000000..dce9563 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-arrayManipulation2.xml @@ -0,0 +1,41 @@ + + + + hello wolrd + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-condition.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-condition.xml new file mode 100644 index 0000000..bcdb2cb --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-condition.xml @@ -0,0 +1,20 @@ + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-dod.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-dod.xml new file mode 100644 index 0000000..643d79a --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-dod.xml @@ -0,0 +1,24 @@ + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-done.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-done.xml new file mode 100644 index 0000000..884b5e5 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-done.xml @@ -0,0 +1,23 @@ + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-failOnSensorError.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-failOnSensorError.xml new file mode 100644 index 0000000..07e88aa --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-failOnSensorError.xml @@ -0,0 +1,30 @@ + + + + + Simple 1D Scan + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-function.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-function.xml new file mode 100644 index 0000000..0808777 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-function.xml @@ -0,0 +1,28 @@ + + + + + + + 0.0 + 4.0 + 0.5 + + + + + + 9 + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-functionVariable.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-functionVariable.xml new file mode 100644 index 0000000..a24cbe1 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-functionVariable.xml @@ -0,0 +1,52 @@ + + + + + + + + + + 0.0 + 4.0 + 0.5 + + + + + + + + 0.0 + 4.0 + 0.5 + + + + + + + + 9 + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-issue18.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-issue18.xml new file mode 100644 index 0000000..2dd32b0 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-issue18.xml @@ -0,0 +1,23 @@ + + + + + + + + 1.0 + 1.0025 + 0.0001 + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-jythonpre.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-jythonpre.xml new file mode 100644 index 0000000..d6c7603 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-jythonpre.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-longPrePut.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-longPrePut.xml new file mode 100644 index 0000000..88a4c5b --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-longPrePut.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulation.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulation.xml new file mode 100644 index 0000000..53f538c --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulation.xml @@ -0,0 +1,42 @@ + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulationChannel-2.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulationChannel-2.xml new file mode 100644 index 0000000..35547b0 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulationChannel-2.xml @@ -0,0 +1,42 @@ + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulationChannel.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulationChannel.xml new file mode 100644 index 0000000..8eaf209 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulationChannel.xml @@ -0,0 +1,44 @@ + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulationOfM.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulationOfM.xml new file mode 100644 index 0000000..114badf --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulationOfM.xml @@ -0,0 +1,34 @@ + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulationTime.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulationTime.xml new file mode 100644 index 0000000..ab90b24 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulationTime.xml @@ -0,0 +1,28 @@ + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulationVariable.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulationVariable.xml new file mode 100644 index 0000000..c5ba83a --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-manipulationVariable.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-mmm.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-mmm.xml new file mode 100644 index 0000000..f080879 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-mmm.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + 0.0 + 8.0 + 0.5 + + + 4 4 4 4 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-noPositioner.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-noPositioner.xml new file mode 100644 index 0000000..99b7642 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-noPositioner.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-noVis.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-noVis.xml new file mode 100644 index 0000000..6e193fd --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-noVis.xml @@ -0,0 +1,19 @@ + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-notification.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-notification.xml new file mode 100644 index 0000000..707f777 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-notification.xml @@ -0,0 +1,29 @@ + + + + + simon.ebner@psi.ch + + + + Simple 1D Scan + + + + + 0.0 + 8.0 + 0.1 + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-pre.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-pre.xml new file mode 100644 index 0000000..13f9ec0 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-pre.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-prePostDimension.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-prePostDimension.xml new file mode 100644 index 0000000..33781fe --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-prePostDimension.xml @@ -0,0 +1,28 @@ + + + + + Simple 1D Scan + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-region.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-region.xml new file mode 100644 index 0000000..7c8605d --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-region.xml @@ -0,0 +1,30 @@ + + + + + + + + + + 0.0 + 8.0 + 0.5 + + + -1.0 + -2.0 + 0.5 + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-regionFunction.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-regionFunction.xml new file mode 100644 index 0000000..28800fc --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-regionFunction.xml @@ -0,0 +1,33 @@ + + + + + + + + + 0.0 + 4.0 + 0.5 + + + -1.0 + -2.0 + 0.5 + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-regionNotExact.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-regionNotExact.xml new file mode 100644 index 0000000..c285080 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-regionNotExact.xml @@ -0,0 +1,30 @@ + + + + + + + + + + 0.0 + 7.8 + 0.5 + + + 8.0 + 9 + 0.1 + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-regionOverlapping.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-regionOverlapping.xml new file mode 100644 index 0000000..630d867 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-regionOverlapping.xml @@ -0,0 +1,30 @@ + + + + + + + + + + 7.0 + 8.1 + 0.1 + + + 8.1 + 10 + 0.5 + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-shellaction.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-shellaction.xml new file mode 100644 index 0000000..ab2b858 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-shellaction.xml @@ -0,0 +1,25 @@ + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-simon.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-simon.xml new file mode 100644 index 0000000..ff4e5cc --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-simon.xml @@ -0,0 +1,20 @@ + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-simon2.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-simon2.xml new file mode 100644 index 0000000..6631069 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-simon2.xml @@ -0,0 +1,19 @@ + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-small.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-small.xml new file mode 100644 index 0000000..b2d185a --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-small.xml @@ -0,0 +1,19 @@ + + + + + + + 0.0 + 0.1 + 0.1 + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-string.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-string.xml new file mode 100644 index 0000000..45a15cf --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-string.xml @@ -0,0 +1,21 @@ + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-waitError.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-waitError.xml new file mode 100644 index 0000000..e1244e2 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d-waitError.xml @@ -0,0 +1,20 @@ + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d.xml new file mode 100644 index 0000000..c13cf86 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d.xml @@ -0,0 +1,19 @@ + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan1d_1234.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d_1234.xml new file mode 100644 index 0000000..a246e54 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan1d_1234.xml @@ -0,0 +1,23 @@ + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-1.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-1.xml new file mode 100644 index 0000000..68f583c --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-1.xml @@ -0,0 +1,26 @@ + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + 1 2 8 9 + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-2.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-2.xml new file mode 100644 index 0000000..6002795 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-2.xml @@ -0,0 +1,29 @@ + + + + + + + + -1 + -0.09 + 0.1 + + + + + + + 3.4 + 3.5 + 0.003 + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-dgroup.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-dgroup.xml new file mode 100644 index 0000000..e1599ea --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-dgroup.xml @@ -0,0 +1,29 @@ + + + + + + + + 0.0 + 4.0 + 1 + + + + + + + 1.0 + -1.0 + 1 + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-guard.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-guard.xml new file mode 100644 index 0000000..c3250ac --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-guard.xml @@ -0,0 +1,28 @@ + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + 1.0 + -2.0 + 0.5 + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-manipulation.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-manipulation.xml new file mode 100644 index 0000000..2a4ce8a --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-manipulation.xml @@ -0,0 +1,41 @@ + + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + 1.0 + -2.0 + 0.5 + + + + + + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-pseudo.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-pseudo.xml new file mode 100644 index 0000000..f19bac1 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-pseudo.xml @@ -0,0 +1,27 @@ + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + 3 + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-pseudo2.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-pseudo2.xml new file mode 100644 index 0000000..df68a71 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-pseudo2.xml @@ -0,0 +1,23 @@ + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + 3 + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-region.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-region.xml new file mode 100644 index 0000000..7f7ab48 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-region.xml @@ -0,0 +1,36 @@ + + + + + + + + + 0.0 + 2.0 + 0.5 + + + 2.0 + 3.0 + 0.5 + + + + + + + + 1.0 + -2.0 + 0.5 + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-zigZag.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-zigZag.xml new file mode 100644 index 0000000..ea89fd8 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-zigZag.xml @@ -0,0 +1,29 @@ + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + 1.0 + -1.0 + 1 + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-zigzag2.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-zigzag2.xml new file mode 100644 index 0000000..eaa6566 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d-zigzag2.xml @@ -0,0 +1,27 @@ + + + + + + + + 1.0 + 1.26 + 0.04 + + + + + + + 5 + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/home/scans/templates/scan2d.xml b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d.xml new file mode 100644 index 0000000..f6e4604 --- /dev/null +++ b/ch.psi.fda/src/test/resources/home/scans/templates/scan2d.xml @@ -0,0 +1,29 @@ + + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + 1.0 + -2.0 + 0.5 + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/jcae.properties b/ch.psi.fda/src/test/resources/jcae.properties new file mode 100644 index 0000000..d6de35c --- /dev/null +++ b/ch.psi.fda/src/test/resources/jcae.properties @@ -0,0 +1,4 @@ +# Test properties file +ch.psi.jcae.ContextFactory.addressList=129.129.130.255 129.129.130.37 129.129.145.26 129.129.130.88 + +ch.psi.jcae.ChannelBeanFactory.retries=4 \ No newline at end of file diff --git a/ch.psi.fda/src/test/resources/logging.properties b/ch.psi.fda/src/test/resources/logging.properties new file mode 100644 index 0000000..606af07 --- /dev/null +++ b/ch.psi.fda/src/test/resources/logging.properties @@ -0,0 +1,14 @@ +# Specify the handlers to create in the root logger +handlers = java.util.logging.ConsoleHandler + +# Set the default logging level for the root logger +.level=ALL + +# Set the default logging level for new ConsoleHandler instances +java.util.logging.ConsoleHandler.level=ALL + +# Set the default formatter for new ConsoleHandler instances +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter + +# Set the default logging level for the logger named com.mycompany +ch.psi.level=ALL diff --git a/ch.psi.fda/src/test/resources/testdata/mda/mdadata1.mda b/ch.psi.fda/src/test/resources/testdata/mda/mdadata1.mda new file mode 100644 index 0000000..6115580 Binary files /dev/null and b/ch.psi.fda/src/test/resources/testdata/mda/mdadata1.mda differ diff --git a/ch.psi.fda/src/test/resources/testdata/mda/mdadata2.mda b/ch.psi.fda/src/test/resources/testdata/mda/mdadata2.mda new file mode 100644 index 0000000..6a2d1a4 Binary files /dev/null and b/ch.psi.fda/src/test/resources/testdata/mda/mdadata2.mda differ diff --git a/ch.psi.fda/src/test/resources/testdata/mda/mdadata3.mda b/ch.psi.fda/src/test/resources/testdata/mda/mdadata3.mda new file mode 100644 index 0000000..324e377 Binary files /dev/null and b/ch.psi.fda/src/test/resources/testdata/mda/mdadata3.mda differ diff --git a/ch.psi.fda/src/test/resources/testdata/mda/mdadata4.mda b/ch.psi.fda/src/test/resources/testdata/mda/mdadata4.mda new file mode 100644 index 0000000..7b635f3 Binary files /dev/null and b/ch.psi.fda/src/test/resources/testdata/mda/mdadata4.mda differ diff --git a/ch.psi.fda/src/test/resources/testdata/mda/mdadata5.mda b/ch.psi.fda/src/test/resources/testdata/mda/mdadata5.mda new file mode 100644 index 0000000..6ac5b42 Binary files /dev/null and b/ch.psi.fda/src/test/resources/testdata/mda/mdadata5.mda differ diff --git a/ch.psi.fda/src/test/resources/testdata/mda/mdadata6.mda b/ch.psi.fda/src/test/resources/testdata/mda/mdadata6.mda new file mode 100644 index 0000000..dfdb0f1 Binary files /dev/null and b/ch.psi.fda/src/test/resources/testdata/mda/mdadata6.mda differ diff --git a/ch.psi.fda/src/test/resources/testdata/mda/mdadata7.mda b/ch.psi.fda/src/test/resources/testdata/mda/mdadata7.mda new file mode 100644 index 0000000..a199fe7 Binary files /dev/null and b/ch.psi.fda/src/test/resources/testdata/mda/mdadata7.mda differ diff --git a/ch.psi.fda/src/test/resources/testdata/scan/scan1d.xml b/ch.psi.fda/src/test/resources/testdata/scan/scan1d.xml new file mode 100644 index 0000000..21e4866 --- /dev/null +++ b/ch.psi.fda/src/test/resources/testdata/scan/scan1d.xml @@ -0,0 +1,19 @@ + + + + + + + 0.0 + 8.0 + 0.5 + + + + + + + + + + diff --git a/ch.psi.fda/src/test/resources/testdata/scan/scan1d_0000.txt b/ch.psi.fda/src/test/resources/testdata/scan/scan1d_0000.txt new file mode 100644 index 0000000..6aee773 --- /dev/null +++ b/ch.psi.fda/src/test/resources/testdata/scan/scan1d_0000.txt @@ -0,0 +1,19 @@ +#id0 idD0 idD1 timestamp +#0 0 0 0 +0.0 0.0 0.0010 1.283931433121E12 +0.5 100.0 0.496 1.283931433703E12 +1.0 201.0 1.0035 1.283931434403E12 +1.5 301.0 1.5005 1.283931434999E12 +2.0 401.0 2.005 1.283931435598E12 +2.5 500.0 2.496 1.283931436197E12 +3.0 601.0 3.0045 1.283931436866E12 +3.5 701.0 3.5015 1.283931437463E12 +4.0 801.0 4.0045 1.283931438063E12 +4.5 901.0 4.5025 1.283931438659E12 +5.0 1000.0 5.002 1.283931439556E12 +5.5 1100.0 5.4975000000000005 1.283931440155E12 +6.0 1201.0 6.0045 1.283931440822E12 +6.5 1301.0 6.5035 1.283931441404E12 +7.0 1401.0 7.0045 1.283931442002E12 +7.5 1501.0 7.505 1.283931442599E12 +8.0 1600.0 8.0 1.283931443198E12 diff --git a/ch.psi.fda/src/test/resources/testdata/text/textdata1.txt b/ch.psi.fda/src/test/resources/testdata/text/textdata1.txt new file mode 100644 index 0000000..42761e8 --- /dev/null +++ b/ch.psi.fda/src/test/resources/testdata/text/textdata1.txt @@ -0,0 +1,597 @@ +#id2 id0 I0_SAI05 I1_SAI06 I2_SAI07 Ring-current ROI0 ROI1 ROI2 ROI3 ROI4 ROI5 ROI6 ROI7 ROI8 ROI9 ROI10 Ref-pulses Timestamp +#1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +1.0 0.00174 1.52590219E-4 7.62951095E-4 0.073701075761 401.194880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999998.0 1.288808677478E12 +1.0 0.00269 1.52590219E-4 7.62951095E-4 0.073701075761 401.179642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000079.0 1.288808677578E12 +1.0 0.00377 1.52590219E-4 7.62951095E-4 0.073701075761 401.179642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000043.0 1.288808677678E12 +1.0 0.00468 1.52590219E-4 7.62951095E-4 0.073701075761 401.179642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000078.0 1.288808677778E12 +1.0 0.00576 1.52590219E-4 7.62951095E-4 0.073701075761 401.168214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000023.0 1.288808677878E12 +1.0 0.00672 1.52590219E-4 7.62951095E-4 0.073701075761 401.168214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000045.0 1.288808677978E12 +1.0 0.00779 1.52590219E-4 7.62951095E-4 0.073701075761 401.168214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999990.0 1.288808678078E12 +1.0 0.00874 1.52590219E-4 7.62951095E-4 0.073701075761 401.156785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000650.0 1.288808678178E12 +1.0 0.00978 1.52590219E-4 7.62951095E-4 0.073701075761 401.156785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000073.0 1.288808678278E12 +1.0 0.01074 1.52590219E-4 7.62951095E-4 0.073701075761 401.156785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999403.0 1.288808678378E12 +1.0 0.01179 1.52590219E-4 7.62951095E-4 0.073701075761 401.149166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000682.0 1.288808678478E12 +1.0 0.01272 1.52590219E-4 7.62951095E-4 0.073701075761 401.149166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999448.0 1.288808678578E12 +1.0 0.01378 1.52590219E-4 7.62951095E-4 0.073701075761 401.149166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000941.0 1.288808678678E12 +1.0 0.01473 1.52590219E-4 7.62951095E-4 0.073701075761 401.149166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999142.0 1.288808678778E12 +1.0 0.01579 1.52590219E-4 7.62951095E-4 0.073701075761 401.137738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000039.0 1.288808678878E12 +1.0 0.01671 1.52590219E-4 7.62951095E-4 0.073701075761 401.137738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000013.0 1.288808678978E12 +1.0 0.01777 1.52590219E-4 7.62951095E-4 0.073701075761 401.137738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000011.0 1.288808679078E12 +1.0 0.01873 1.52590219E-4 7.62951095E-4 0.073701075761 401.130119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000687.0 1.288808679178E12 +1.0 0.01978 1.52590219E-4 7.62951095E-4 0.073701075761 401.130119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999442.0 1.288808679278E12 +1.0 0.02068 1.52590219E-4 7.62951095E-4 0.073701075761 401.130119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000007.0 1.288808679378E12 +1.0 0.02173 1.52590219E-4 7.62951095E-4 0.073701075761 401.118690476191 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000031.0 1.288808679478E12 +1.0 0.02269 1.52590219E-4 7.62951095E-4 0.073701075761 401.118690476191 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000144.0 1.288808679578E12 +1.0 0.02376 1.52590219E-4 7.62951095E-4 0.073701075761 401.118690476191 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000610.0 1.288808679678E12 +1.0 0.02467 1.52590219E-4 7.62951095E-4 0.073701075761 401.107261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999454.0 1.288808679778E12 +1.0 0.02573 1.52590219E-4 7.62951095E-4 0.073701075761 401.107261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000052.0 1.288808679878E12 +1.0 0.02669 1.52590219E-4 7.62951095E-4 0.073701075761 401.107261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999979.0 1.288808679978E12 +1.0 0.02777 1.52590219E-4 7.62951095E-4 0.073701075761 401.099642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000675.0 1.288808680078E12 +1.0 0.02868 1.52590219E-4 7.62951095E-4 0.073701075761 401.099642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999415.0 1.288808680178E12 +1.0 0.02973 1.52590219E-4 7.62951095E-4 0.073701075761 401.099642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000039.0 1.288808680278E12 +1.0 0.03067 1.52590219E-4 7.62951095E-4 0.073701075761 401.099642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000036.0 1.288808680378E12 +1.0 0.03173 1.52590219E-4 7.62951095E-4 0.073701075761 401.092023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000664.0 1.288808680478E12 +1.0 0.03264 1.52590219E-4 7.62951095E-4 0.073701075761 401.092023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999723.0 1.288808680578E12 +1.0 0.0337 1.52590219E-4 7.62951095E-4 0.073701075761 401.092023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999867.0 1.288808680678E12 +1.0 0.03466 1.52590219E-4 7.62951095E-4 0.073701075761 401.080595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000004.0 1.288808680778E12 +1.0 0.03574 1.52590219E-4 7.62951095E-4 0.073701075761 401.080595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000047.0 1.288808680878E12 +1.0 0.03665 1.52590219E-4 7.62951095E-4 0.073395895323 401.080595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999998.0 1.288808680978E12 +1.0 0.03771 1.52590219E-4 7.62951095E-4 0.073395895323 401.072976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000076.0 1.288808681078E12 +1.0 0.03867 1.52590219E-4 7.62951095E-4 0.073701075761 401.072976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000006.0 1.288808681178E12 +1.0 0.03974 1.52590219E-4 7.62951095E-4 0.073701075761 401.072976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000640.0 1.288808681278E12 +1.0 0.04065 1.52590219E-4 7.62951095E-4 0.073395895323 401.061547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999417.0 1.288808681378E12 +1.0 0.04171 1.52590219E-4 7.62951095E-4 0.073395895323 401.061547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000639.0 1.288808681478E12 +1.0 0.04267 1.52590219E-4 7.62951095E-4 0.073395895323 401.061547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999422.0 1.288808681578E12 +1.0 0.04376 1.52590219E-4 7.62951095E-4 0.073395895323 401.053928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000669.0 1.288808681678E12 +1.0 0.04466 1.52590219E-4 7.62951095E-4 0.073701075761 401.053928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999506.0 1.288808681778E12 +1.0 0.04572 1.52590219E-4 7.62951095E-4 0.073701075761 401.053928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000017.0 1.288808681878E12 +1.0 0.04667 1.52590219E-4 7.62951095E-4 0.073701075761 401.053928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000067.0 1.288808681978E12 +1.0 0.04776 1.52590219E-4 7.62951095E-4 0.073701075761 401.0425 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999979.0 1.288808682078E12 +1.0 0.04867 1.52590219E-4 7.62951095E-4 0.073701075761 401.0425 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000670.0 1.288808682178E12 +1.0 0.04972 1.52590219E-4 7.62951095E-4 0.073701075761 401.0425 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999496.0 1.288808682278E12 +1.0 0.05066 1.52590219E-4 7.62951095E-4 0.073701075761 401.031071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000346.0 1.288808682378E12 +1.0 0.05172 1.52590219E-4 7.62951095E-4 0.073701075761 401.031071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999660.0 1.288808682478E12 +1.0 0.05263 1.52590219E-4 7.62951095E-4 0.073395895323 401.031071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000060.0 1.288808682578E12 +1.0 0.05369 1.52590219E-4 7.62951095E-4 0.073395895323 401.019642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000034.0 1.288808682678E12 +1.0 0.05465 1.52590219E-4 7.62951095E-4 0.073701075761 401.019642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000043.0 1.288808682778E12 +1.0 0.05573 1.52590219E-4 7.62951095E-4 0.073701075761 401.019642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000142.0 1.288808682878E12 +1.0 0.05664 1.52590219E-4 7.62951095E-4 0.073701075761 401.008214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999993.0 1.288808682978E12 +1.0 0.05771 1.52590219E-4 7.62951095E-4 0.073701075761 401.008214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999973.0 1.288808683078E12 +1.0 0.05866 1.52590219E-4 7.62951095E-4 0.073701075761 401.008214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000059.0 1.288808683178E12 +1.0 0.05973 1.52590219E-4 7.62951095E-4 0.073701075761 400.996785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000033.0 1.288808683278E12 +1.0 0.06063 1.52590219E-4 7.62951095E-4 0.073701075761 400.996785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000104.0 1.288808683378E12 +1.0 0.06168 1.52590219E-4 7.62951095E-4 0.073701075761 400.996785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000463.0 1.288808683478E12 +1.0 0.06262 1.52590219E-4 7.62951095E-4 0.073701075761 400.996785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999569.0 1.288808683578E12 +1.0 0.06368 1.52590219E-4 7.62951095E-4 0.073701075761 400.981547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000044.0 1.288808683678E12 +1.0 0.06458 1.52590219E-4 7.62951095E-4 0.073701075761 400.981547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000048.0 1.288808683778E12 +1.0 0.06562 1.52590219E-4 7.62951095E-4 0.073701075761 400.981547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000088.0 1.288808683878E12 +1.0 0.06658 1.52590219E-4 7.62951095E-4 0.073701075761 400.970119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999984.0 1.288808683978E12 +1.0 0.06764 1.52590219E-4 7.62951095E-4 0.073701075761 400.970119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000050.0 1.288808684078E12 +1.0 0.06858 1.52590219E-4 7.62951095E-4 0.073395895323 400.970119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000094.0 1.288808684178E12 +1.0 0.06962 1.52590219E-4 7.62951095E-4 0.073395895323 400.95869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000007.0 1.288808684278E12 +1.0 0.07059 1.52590219E-4 7.62951095E-4 0.073701075761 400.95869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000036.0 1.288808684378E12 +1.0 0.07166 1.52590219E-4 7.62951095E-4 0.073701075761 400.95869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000059.0 1.288808684478E12 +1.0 0.07259 1.52590219E-4 7.62951095E-4 0.073701075761 400.947261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000001.0 1.288808684578E12 +1.0 0.07366 1.52590219E-4 7.62951095E-4 0.073701075761 400.947261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000072.0 1.288808684678E12 +1.0 0.07462 1.52590219E-4 7.62951095E-4 0.073701075761 400.947261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000022.0 1.288808684778E12 +1.0 0.07572 1.52590219E-4 7.62951095E-4 0.073701075761 400.935833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000148.0 1.288808684878E12 +1.0 0.07663 1.52590219E-4 7.62951095E-4 0.073701075761 400.935833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999954.0 1.288808684978E12 +1.0 0.0777 1.52590219E-4 7.62951095E-4 0.073701075761 400.935833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000035.0 1.288808685078E12 +1.0 0.07866 1.52590219E-4 7.62951095E-4 0.073395895323 400.935833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000108.0 1.288808685178E12 +1.0 0.07972 1.52590219E-4 7.62951095E-4 0.073395895323 400.920595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000065.0 1.288808685278E12 +1.0 0.08061 1.52590219E-4 7.62951095E-4 0.073701075761 400.920595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000242.0 1.288808685378E12 +1.0 0.08165 1.52590219E-4 7.62951095E-4 0.073701075761 400.920595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999754.0 1.288808685478E12 +1.0 0.08261 1.52590219E-4 7.62951095E-4 0.073395895323 400.909166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000408.0 1.288808685578E12 +1.0 0.08368 1.52590219E-4 7.62951095E-4 0.073395895323 400.909166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999677.0 1.288808685678E12 +1.0 0.08459 1.52590219E-4 7.62951095E-4 0.073701075761 400.909166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000087.0 1.288808685778E12 +1.0 0.08563 1.52590219E-4 7.62951095E-4 0.073701075761 400.897738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000282.0 1.288808685878E12 +1.0 0.0866 1.52590219E-4 7.62951095E-4 0.073701075761 400.897738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999827.0 1.288808685978E12 +1.0 0.0877 1.52590219E-4 7.62951095E-4 0.073701075761 400.897738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999961.0 1.288808686078E12 +1.0 0.08861 1.52590219E-4 7.62951095E-4 0.073701075761 400.88630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000132.0 1.288808686178E12 +1.0 0.08968 1.52590219E-4 7.62951095E-4 0.073701075761 400.88630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000397.0 1.288808686278E12 +1.0 0.09061 1.52590219E-4 7.62951095E-4 0.073395895323 400.88630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999593.0 1.288808686378E12 +1.0 0.09168 1.52590219E-4 7.62951095E-4 0.073395895323 400.87869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000058.0 1.288808686478E12 +1.0 0.09259 1.52590219E-4 7.62951095E-4 0.073701075761 400.87869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000018.0 1.288808686578E12 +1.0 0.09364 1.52590219E-4 7.62951095E-4 0.073701075761 400.87869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000060.0 1.288808686678E12 +1.0 0.0946 1.52590219E-4 7.62951095E-4 0.073701075761 400.87869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000545.0 1.288808686778E12 +1.0 0.0957 1.52590219E-4 7.62951095E-4 0.073701075761 400.867261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999583.0 1.288808686878E12 +1.0 0.0966 1.52590219E-4 7.62951095E-4 0.073395895323 400.867261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999992.0 1.288808686978E12 +1.0 0.09767 1.52590219E-4 7.62951095E-4 0.073395895323 400.867261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000050.0 1.288808687078E12 +1.0 0.09863 1.52590219E-4 7.62951095E-4 0.073701075761 400.859642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000069.0 1.288808687178E12 +1.0 0.0997 1.52590219E-4 7.62951095E-4 0.073701075761 400.859642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000090.0 1.288808687278E12 +1.002 0.00149 1.52590219E-4 7.62951095E-4 0.073701075761 400.760595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000006.0 1.288808690493E12 +1.002 0.00253 1.52590219E-4 7.62951095E-4 0.073701075761 400.760595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999978.0 1.288808690593E12 +1.002 0.00351 1.52590219E-4 7.62951095E-4 0.073701075761 400.749166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000031.0 1.288808690693E12 +1.002 0.00452 1.52590219E-4 7.62951095E-4 0.073701075761 400.749166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000013.0 1.288808690793E12 +1.002 0.00549 1.52590219E-4 7.62951095E-4 0.073701075761 400.749166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000125.0 1.288808690893E12 +1.002 0.00655 1.52590219E-4 7.62951095E-4 0.073701075761 400.737738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000010.0 1.288808690993E12 +1.002 0.00754 1.52590219E-4 7.62951095E-4 0.073701075761 400.737738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000064.0 1.288808691093E12 +1.002 0.00857 1.52590219E-4 7.62951095E-4 0.073701075761 400.737738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000047.0 1.288808691193E12 +1.002 0.00953 1.52590219E-4 7.62951095E-4 0.073701075761 400.7225 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1002512.0 1.288808691293E12 +1.002 0.01056 1.52590219E-4 7.62951095E-4 0.073701075761 400.7225 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 997579.0 1.288808691393E12 +1.002 0.01154 1.52590219E-4 7.62951095E-4 0.073701075761 400.7225 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999930.0 1.288808691493E12 +1.002 0.01255 1.52590219E-4 7.62951095E-4 0.073701075761 400.711071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000115.0 1.288808691593E12 +1.002 0.01351 1.52590219E-4 7.62951095E-4 0.073701075761 400.711071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000100.0 1.288808691693E12 +1.002 0.01455 1.52590219E-4 7.62951095E-4 0.073701075761 400.711071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999902.0 1.288808691793E12 +1.002 0.01556 1.52590219E-4 7.62951095E-4 0.073701075761 400.711071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000162.0 1.288808691893E12 +1.002 0.01657 1.52590219E-4 7.62951095E-4 0.073701075761 400.699642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000044.0 1.288808691993E12 +1.002 0.01752 1.52590219E-4 7.62951095E-4 0.073701075761 400.699642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000048.0 1.288808692093E12 +1.002 0.01858 1.52590219E-4 7.62951095E-4 0.073701075761 400.699642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000028.0 1.288808692193E12 +1.002 0.01958 1.52590219E-4 7.62951095E-4 0.073701075761 400.688214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1002546.0 1.288808692293E12 +1.002 0.02057 1.52590219E-4 7.62951095E-4 0.073701075761 400.688214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 997491.0 1.288808692393E12 +1.002 0.02151 1.52590219E-4 7.62951095E-4 0.073701075761 400.688214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000113.0 1.288808692493E12 +1.002 0.02256 1.52590219E-4 7.62951095E-4 0.073701075761 400.676785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999969.0 1.288808692593E12 +1.002 0.02354 1.52590219E-4 7.62951095E-4 0.073701075761 400.676785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000043.0 1.288808692693E12 +1.002 0.02453 1.52590219E-4 7.62951095E-4 0.073701075761 400.676785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999974.0 1.288808692793E12 +1.002 0.02548 1.52590219E-4 7.62951095E-4 0.073701075761 400.665357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000068.0 1.288808692893E12 +1.002 0.02653 1.52590219E-4 7.62951095E-4 0.073701075761 400.665357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000044.0 1.288808692993E12 +1.002 0.02753 1.52590219E-4 7.62951095E-4 0.073701075761 400.665357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000150.0 1.288808693093E12 +1.002 0.02856 1.52590219E-4 7.62951095E-4 0.073701075761 400.650119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999995.0 1.288808693193E12 +1.002 0.02952 1.52590219E-4 7.62951095E-4 0.073701075761 400.650119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1002584.0 1.288808693293E12 +1.002 0.03055 1.52590219E-4 7.62951095E-4 0.073701075761 400.650119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 997533.0 1.288808693393E12 +1.002 0.03151 1.52590219E-4 7.62951095E-4 0.073701075761 400.650119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999995.0 1.288808693493E12 +1.002 0.03253 1.52590219E-4 7.62951095E-4 0.073701075761 400.63869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000049.0 1.288808693593E12 +1.002 0.03348 1.52590219E-4 7.62951095E-4 0.073701075761 400.63869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000220.0 1.288808693693E12 +1.002 0.03453 1.52590219E-4 7.62951095E-4 0.073701075761 400.63869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999795.0 1.288808693793E12 +1.002 0.03552 1.52590219E-4 7.62951095E-4 0.073395895323 400.627261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000108.0 1.288808693893E12 +1.002 0.03653 1.52590219E-4 7.62951095E-4 0.073395895323 400.627261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000070.0 1.288808693993E12 +1.002 0.03748 1.52590219E-4 7.62951095E-4 0.073395895323 400.627261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000083.0 1.288808694093E12 +1.002 0.03854 1.52590219E-4 7.62951095E-4 0.073395895323 400.615833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000041.0 1.288808694193E12 +1.002 0.03952 1.52590219E-4 7.62951095E-4 0.073701075761 400.615833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000063.0 1.288808694293E12 +1.002 0.04052 1.52590219E-4 7.62951095E-4 0.073701075761 400.615833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999951.0 1.288808694393E12 +1.002 0.04146 1.52590219E-4 7.62951095E-4 0.073701075761 400.604404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000106.0 1.288808694493E12 +1.002 0.0425 1.52590219E-4 7.62951095E-4 0.073701075761 400.604404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000009.0 1.288808694593E12 +1.002 0.04348 1.52590219E-4 7.62951095E-4 0.073701075761 400.604404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000009.0 1.288808694693E12 +1.002 0.0445 1.52590219E-4 7.62951095E-4 0.073701075761 400.604404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999984.0 1.288808694793E12 +1.002 0.04546 1.52590219E-4 7.62951095E-4 0.073701075761 400.596785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000063.0 1.288808694893E12 +1.002 0.04651 1.52590219E-4 7.62951095E-4 0.073701075761 400.596785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000142.0 1.288808694993E12 +1.002 0.0475 1.52590219E-4 7.62951095E-4 0.073701075761 400.596785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999975.0 1.288808695093E12 +1.002 0.04852 1.52590219E-4 7.62951095E-4 0.073701075761 400.585357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000093.0 1.288808695193E12 +1.002 0.04948 1.52590219E-4 7.62951095E-4 0.073701075761 400.585357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1002554.0 1.288808695293E12 +1.002 0.05051 1.52590219E-4 7.62951095E-4 0.073701075761 400.585357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 997559.0 1.288808695393E12 +1.002 0.05148 1.52590219E-4 7.62951095E-4 0.073701075761 400.577738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000054.0 1.288808695493E12 +1.002 0.0525 1.52590219E-4 7.62951095E-4 0.073701075761 400.577738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999976.0 1.288808695593E12 +1.002 0.05345 1.52590219E-4 7.62951095E-4 0.073701075761 400.577738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000093.0 1.288808695693E12 +1.002 0.0545 1.52590219E-4 7.62951095E-4 0.073701075761 400.56630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999924.0 1.288808695793E12 +1.002 0.05549 1.52590219E-4 7.62951095E-4 0.073701075761 400.56630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000165.0 1.288808695893E12 +1.002 0.05651 1.52590219E-4 7.62951095E-4 0.073701075761 400.56630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000029.0 1.288808695993E12 +1.002 0.05746 1.52590219E-4 7.62951095E-4 0.073701075761 400.55869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000043.0 1.288808696093E12 +1.002 0.05853 1.52590219E-4 7.62951095E-4 0.073701075761 400.55869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000000.0 1.288808696193E12 +1.002 0.0595 1.52590219E-4 7.62951095E-4 0.073701075761 400.55869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1002494.0 1.288808696293E12 +1.002 0.0605 1.52590219E-4 7.62951095E-4 0.073701075761 400.551071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 997610.0 1.288808696393E12 +1.002 0.06143 1.52590219E-4 7.62951095E-4 0.073701075761 400.551071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000037.0 1.288808696493E12 +1.002 0.06247 1.52590219E-4 7.62951095E-4 0.073701075761 400.551071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000023.0 1.288808696593E12 +1.002 0.06344 1.52590219E-4 7.62951095E-4 0.073395895323 400.551071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000055.0 1.288808696693E12 +1.002 0.06442 1.52590219E-4 7.62951095E-4 0.073395895323 400.539642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999963.0 1.288808696793E12 +1.002 0.06539 1.52590219E-4 7.62951095E-4 0.073701075761 400.539642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000131.0 1.288808696893E12 +1.002 0.06643 1.52590219E-4 7.62951095E-4 0.073701075761 400.539642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999975.0 1.288808696993E12 +1.002 0.06742 1.52590219E-4 7.62951095E-4 0.073701075761 400.532023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000086.0 1.288808697093E12 +1.002 0.06844 1.52590219E-4 7.62951095E-4 0.073701075761 400.532023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000098.0 1.288808697193E12 +1.002 0.0694 1.52590219E-4 7.62951095E-4 0.073701075761 400.532023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999999.0 1.288808697293E12 +1.002 0.07043 1.52590219E-4 7.62951095E-4 0.073701075761 400.520595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000091.0 1.288808697393E12 +1.002 0.07141 1.52590219E-4 7.62951095E-4 0.073701075761 400.520595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999931.0 1.288808697493E12 +1.002 0.07242 1.52590219E-4 7.62951095E-4 0.073701075761 400.520595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000106.0 1.288808697593E12 +1.002 0.0734 1.52590219E-4 7.62951095E-4 0.073701075761 400.512976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000100.0 1.288808697693E12 +1.002 0.07444 1.52590219E-4 7.62951095E-4 0.073701075761 400.512976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999916.0 1.288808697793E12 +1.002 0.07545 1.52590219E-4 7.62951095E-4 0.073701075761 400.512976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000144.0 1.288808697893E12 +1.002 0.07646 1.52590219E-4 7.62951095E-4 0.073701075761 400.501547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000020.0 1.288808697993E12 +1.002 0.0774 1.52590219E-4 7.62951095E-4 0.073701075761 400.501547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000076.0 1.288808698093E12 +1.002 0.07846 1.52590219E-4 7.62951095E-4 0.073701075761 400.501547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000048.0 1.288808698193E12 +1.002 0.07945 1.52590219E-4 7.62951095E-4 0.073701075761 400.501547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1002530.0 1.288808698293E12 +1.002 0.08044 1.52590219E-4 7.62951095E-4 0.073701075761 400.490119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 997556.0 1.288808698393E12 +1.002 0.08139 1.52590219E-4 7.62951095E-4 0.073701075761 400.490119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000057.0 1.288808698493E12 +1.002 0.08241 1.52590219E-4 7.62951095E-4 0.073701075761 400.490119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000033.0 1.288808698593E12 +1.002 0.0834 1.52590219E-4 7.62951095E-4 0.073701075761 400.47869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999981.0 1.288808698693E12 +1.002 0.08439 1.52590219E-4 7.62951095E-4 0.073701075761 400.47869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999984.0 1.288808698793E12 +1.002 0.08538 1.52590219E-4 7.62951095E-4 0.073701075761 400.47869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000088.0 1.288808698893E12 +1.002 0.0864 1.52590219E-4 7.62951095E-4 0.073701075761 400.467261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000013.0 1.288808698993E12 +1.002 0.08741 1.52590219E-4 7.62951095E-4 0.073701075761 400.467261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000138.0 1.288808699093E12 +1.002 0.08843 1.52590219E-4 7.62951095E-4 0.073701075761 400.467261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000064.0 1.288808699193E12 +1.002 0.0894 1.52590219E-4 7.62951095E-4 0.073701075761 400.455833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1002237.0 1.288808699293E12 +1.002 0.09041 1.52590219E-4 7.62951095E-4 0.073701075761 400.455833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 997818.0 1.288808699393E12 +1.002 0.0914 1.52590219E-4 7.62951095E-4 0.073701075761 400.455833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000067.0 1.288808699493E12 +1.002 0.09238 1.52590219E-4 7.62951095E-4 0.073701075761 400.440595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999989.0 1.288808699593E12 +1.002 0.09338 1.52590219E-4 7.62951095E-4 0.073701075761 400.440595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000091.0 1.288808699693E12 +1.002 0.09439 1.52590219E-4 7.62951095E-4 0.073701075761 400.440595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999925.0 1.288808699793E12 +1.002 0.0954 1.52590219E-4 7.62951095E-4 0.073395895323 400.440595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000119.0 1.288808699893E12 +1.002 0.09639 1.52590219E-4 7.62951095E-4 0.073395895323 400.429166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000052.0 1.288808699993E12 +1.002 0.09738 1.52590219E-4 7.62951095E-4 0.073701075761 400.429166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000039.0 1.288808700093E12 +1.002 0.0984 1.52590219E-4 7.62951095E-4 0.073701075761 400.429166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000078.0 1.288808700193E12 +1.002 0.09939 1.52590219E-4 7.62951095E-4 0.073701075761 400.417738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000003.0 1.288808700293E12 +1.004 0.00147 1.52590219E-4 7.62951095E-4 0.073701075761 400.284404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999994.0 1.288808704508E12 +1.004 0.00251 1.52590219E-4 7.62951095E-4 0.073701075761 400.284404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000081.0 1.288808704608E12 +1.004 0.00349 1.52590219E-4 7.62951095E-4 0.073701075761 400.272976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000074.0 1.288808704708E12 +1.004 0.00451 1.52590219E-4 7.62951095E-4 0.073701075761 400.272976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000031.0 1.288808704808E12 +1.004 0.00547 1.52590219E-4 7.62951095E-4 0.073701075761 400.272976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000031.0 1.288808704908E12 +1.004 0.00654 1.52590219E-4 7.62951095E-4 0.073701075761 400.272976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000011.0 1.288808705008E12 +1.004 0.00754 1.52590219E-4 7.62951095E-4 0.073701075761 400.265357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000096.0 1.288808705108E12 +1.004 0.00857 1.52590219E-4 7.62951095E-4 0.073701075761 400.265357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000051.0 1.288808705208E12 +1.004 0.00952 1.52590219E-4 7.62951095E-4 0.073701075761 400.265357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000057.0 1.288808705308E12 +1.004 0.01056 1.52590219E-4 7.62951095E-4 0.073701075761 400.253928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000008.0 1.288808705408E12 +1.004 0.01153 1.52590219E-4 7.62951095E-4 0.073701075761 400.253928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000055.0 1.288808705508E12 +1.004 0.01255 1.52590219E-4 7.62951095E-4 0.073701075761 400.253928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000035.0 1.288808705608E12 +1.004 0.01351 1.52590219E-4 7.62951095E-4 0.073701075761 400.24630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000117.0 1.288808705708E12 +1.004 0.01456 1.52590219E-4 7.62951095E-4 0.073701075761 400.24630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999971.0 1.288808705808E12 +1.004 0.01556 1.52590219E-4 7.62951095E-4 0.073701075761 400.24630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000042.0 1.288808705908E12 +1.004 0.01656 1.52590219E-4 7.62951095E-4 0.073701075761 400.234880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000044.0 1.288808706008E12 +1.004 0.01751 1.52590219E-4 7.62951095E-4 0.073701075761 400.234880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000024.0 1.288808706108E12 +1.004 0.01858 1.52590219E-4 7.62951095E-4 0.073701075761 400.234880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000084.0 1.288808706208E12 +1.004 0.01956 1.52590219E-4 7.62951095E-4 0.073701075761 400.223452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000049.0 1.288808706308E12 +1.004 0.02055 1.52590219E-4 7.62951095E-4 0.073701075761 400.223452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000011.0 1.288808706408E12 +1.004 0.02149 1.52590219E-4 7.62951095E-4 0.073701075761 400.223452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000044.0 1.288808706508E12 +1.004 0.02254 1.52590219E-4 7.62951095E-4 0.073701075761 400.223452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000022.0 1.288808706608E12 +1.004 0.02352 1.52590219E-4 7.62951095E-4 0.073701075761 400.212023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000084.0 1.288808706708E12 +1.004 0.02453 1.52590219E-4 7.62951095E-4 0.073395895323 400.212023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000018.0 1.288808706808E12 +1.004 0.02549 1.52590219E-4 7.62951095E-4 0.073395895323 400.212023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000002.0 1.288808706908E12 +1.004 0.02655 1.52590219E-4 7.62951095E-4 0.073701075761 400.200595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000046.0 1.288808707008E12 +1.004 0.02754 1.52590219E-4 7.62951095E-4 0.073701075761 400.200595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000070.0 1.288808707108E12 +1.004 0.02856 1.52590219E-4 7.62951095E-4 0.073701075761 400.200595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000047.0 1.288808707208E12 +1.004 0.02951 1.52590219E-4 7.62951095E-4 0.073701075761 400.189166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000061.0 1.288808707308E12 +1.004 0.03054 1.52590219E-4 7.62951095E-4 0.073701075761 400.189166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000019.0 1.288808707408E12 +1.004 0.03151 1.52590219E-4 7.62951095E-4 0.073701075761 400.189166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000030.0 1.288808707508E12 +1.004 0.03253 1.52590219E-4 7.62951095E-4 0.073395895323 400.173928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000045.0 1.288808707608E12 +1.004 0.03349 1.52590219E-4 7.62951095E-4 0.073395895323 400.173928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000215.0 1.288808707708E12 +1.004 0.03455 1.52590219E-4 7.62951095E-4 0.073701075761 400.173928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999883.0 1.288808707808E12 +1.004 0.03554 1.52590219E-4 7.62951095E-4 0.073701075761 400.1625 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000077.0 1.288808707909E12 +1.004 0.03655 1.52590219E-4 7.62951095E-4 0.073395895323 400.1625 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000000.0 1.288808708009E12 +1.004 0.03751 1.52590219E-4 7.62951095E-4 0.073395895323 400.1625 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000040.0 1.288808708109E12 +1.004 0.03856 1.52590219E-4 7.62951095E-4 0.073395895323 400.1625 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000053.0 1.288808708209E12 +1.004 0.03954 1.52590219E-4 7.62951095E-4 0.073395895323 400.151071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000104.0 1.288808708309E12 +1.004 0.04052 1.52590219E-4 7.62951095E-4 0.073395895323 400.151071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999955.0 1.288808708409E12 +1.004 0.04146 1.52590219E-4 7.62951095E-4 0.073395895323 400.151071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000057.0 1.288808708509E12 +1.004 0.0425 1.52590219E-4 7.62951095E-4 0.073701075761 400.135833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000040.0 1.288808708609E12 +1.004 0.04347 1.52590219E-4 7.62951095E-4 0.073701075761 400.135833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000078.0 1.288808708709E12 +1.004 0.04448 1.52590219E-4 7.62951095E-4 0.073701075761 400.135833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000002.0 1.288808708809E12 +1.004 0.04544 1.52590219E-4 7.62951095E-4 0.073701075761 400.124404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000049.0 1.288808708909E12 +1.004 0.0465 1.52590219E-4 7.62951095E-4 0.073701075761 400.124404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000006.0 1.288808709009E12 +1.004 0.04749 1.52590219E-4 7.62951095E-4 0.073701075761 400.124404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000067.0 1.288808709109E12 +1.004 0.04852 1.52590219E-4 7.62951095E-4 0.073701075761 400.112976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000053.0 1.288808709209E12 +1.004 0.04948 1.52590219E-4 7.62951095E-4 0.073701075761 400.112976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000126.0 1.288808709309E12 +1.004 0.05051 1.52590219E-4 7.62951095E-4 0.073701075761 400.112976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999940.0 1.288808709409E12 +1.004 0.05149 1.52590219E-4 7.62951095E-4 0.073701075761 400.101547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000057.0 1.288808709509E12 +1.004 0.05251 1.52590219E-4 7.62951095E-4 0.073701075761 400.101547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000034.0 1.288808709609E12 +1.004 0.05347 1.52590219E-4 7.62951095E-4 0.073701075761 400.101547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000073.0 1.288808709709E12 +1.004 0.05451 1.52590219E-4 7.62951095E-4 0.073701075761 400.101547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000023.0 1.288808709809E12 +1.004 0.05549 1.52590219E-4 7.62951095E-4 0.073701075761 400.090119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000046.0 1.288808709909E12 +1.004 0.05651 1.52590219E-4 7.62951095E-4 0.073701075761 400.090119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000025.0 1.288808710009E12 +1.004 0.05745 1.52590219E-4 7.62951095E-4 0.073701075761 400.090119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000031.0 1.288808710109E12 +1.004 0.05851 1.52590219E-4 7.62951095E-4 0.073701075761 400.074880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000076.0 1.288808710209E12 +1.004 0.05948 1.52590219E-4 7.62951095E-4 0.073701075761 400.074880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000102.0 1.288808710309E12 +1.004 0.06048 1.52590219E-4 7.62951095E-4 0.073395895323 400.074880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999976.0 1.288808710409E12 +1.004 0.06142 1.52590219E-4 7.62951095E-4 0.073395895323 400.067261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000004.0 1.288808710509E12 +1.004 0.06246 1.52590219E-4 7.62951095E-4 0.073701075761 400.067261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000079.0 1.288808710609E12 +1.004 0.06343 1.52590219E-4 7.62951095E-4 0.073701075761 400.067261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000073.0 1.288808710709E12 +1.004 0.06441 1.52590219E-4 7.62951095E-4 0.073701075761 400.059642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000012.0 1.288808710809E12 +1.004 0.06538 1.52590219E-4 7.62951095E-4 0.073701075761 400.059642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000028.0 1.288808710909E12 +1.004 0.06639 1.52590219E-4 7.62951095E-4 0.073701075761 400.059642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999993.0 1.288808711009E12 +1.004 0.0674 1.52590219E-4 7.62951095E-4 0.073701075761 400.048214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000124.0 1.288808711109E12 +1.004 0.06841 1.52590219E-4 7.62951095E-4 0.073701075761 400.048214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000027.0 1.288808711209E12 +1.004 0.06939 1.52590219E-4 7.62951095E-4 0.073701075761 400.048214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000108.0 1.288808711309E12 +1.004 0.0704 1.52590219E-4 7.62951095E-4 0.073701075761 400.048214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999960.0 1.288808711409E12 +1.004 0.07139 1.52590219E-4 7.62951095E-4 0.073701075761 400.040595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000041.0 1.288808711509E12 +1.004 0.07238 1.52590219E-4 7.62951095E-4 0.073701075761 400.040595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000047.0 1.288808711609E12 +1.004 0.07338 1.52590219E-4 7.62951095E-4 0.073701075761 400.040595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000012.0 1.288808711709E12 +1.004 0.07439 1.52590219E-4 7.62951095E-4 0.073701075761 400.029166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000079.0 1.288808711809E12 +1.004 0.0754 1.52590219E-4 7.62951095E-4 0.073701075761 400.029166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000053.0 1.288808711909E12 +1.004 0.07638 1.52590219E-4 7.62951095E-4 0.073701075761 400.029166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000025.0 1.288808712009E12 +1.004 0.07738 1.52590219E-4 7.62951095E-4 0.073701075761 400.021547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000025.0 1.288808712109E12 +1.004 0.07839 1.52590219E-4 7.62951095E-4 0.073395895323 400.021547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000082.0 1.288808712209E12 +1.004 0.07939 1.52590219E-4 7.62951095E-4 0.073395895323 400.021547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000093.0 1.288808712309E12 +1.004 0.08038 1.52590219E-4 7.62951095E-4 0.073395895323 400.010119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999979.0 1.288808712409E12 +1.004 0.08137 1.52590219E-4 7.62951095E-4 0.073395895323 400.010119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000047.0 1.288808712509E12 +1.004 0.08236 1.52590219E-4 7.62951095E-4 0.073701075761 400.010119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000044.0 1.288808712609E12 +1.004 0.08338 1.52590219E-4 7.62951095E-4 0.073701075761 400.0025 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000072.0 1.288808712709E12 +1.004 0.08434 1.52590219E-4 7.62951095E-4 0.073701075761 400.0025 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000028.0 1.288808712809E12 +1.004 0.08536 1.52590219E-4 7.62951095E-4 0.073701075761 400.0025 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000027.0 1.288808712909E12 +1.004 0.08634 1.52590219E-4 7.62951095E-4 0.073701075761 400.0025 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000009.0 1.288808713009E12 +1.004 0.08738 1.52590219E-4 7.62951095E-4 0.073701075761 399.991049618321 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000092.0 1.288808713109E12 +1.004 0.08835 1.52590219E-4 7.62951095E-4 0.073395895323 399.991049618321 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000062.0 1.288808713209E12 +1.004 0.08937 1.52590219E-4 7.62951095E-4 0.073395895323 399.991049618321 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000012.0 1.288808713309E12 +1.004 0.09034 1.52590219E-4 7.62951095E-4 0.073395895323 399.983416030534 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000022.0 1.288808713409E12 +1.004 0.09137 1.52590219E-4 7.62951095E-4 0.073395895323 399.983416030534 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000057.0 1.288808713509E12 +1.004 0.09232 1.52590219E-4 7.62951095E-4 0.073395895323 399.983416030534 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000058.0 1.288808713609E12 +1.004 0.09335 1.52590219E-4 7.62951095E-4 0.073395895323 399.971965648855 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000032.0 1.288808713709E12 +1.004 0.09435 1.52590219E-4 7.62951095E-4 0.073395895323 399.971965648855 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000036.0 1.288808713809E12 +1.004 0.09538 1.52590219E-4 7.62951095E-4 0.073395895323 399.971965648855 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000056.0 1.288808713909E12 +1.004 0.09635 1.52590219E-4 7.62951095E-4 0.073701075761 399.960515267176 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000048.0 1.288808714009E12 +1.004 0.09737 1.52590219E-4 7.62951095E-4 0.073701075761 399.960515267176 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000027.0 1.288808714109E12 +1.004 0.09837 1.52590219E-4 7.62951095E-4 0.073701075761 399.960515267176 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000062.0 1.288808714209E12 +1.004 0.09938 1.52590219E-4 7.62951095E-4 0.073701075761 399.949064885496 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000034.0 1.288808714309E12 +1.006 0.00158 1.52590219E-4 7.62951095E-4 0.073701075761 400.707261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000047.0 1.288808718523E12 +1.006 0.00258 1.52590219E-4 7.62951095E-4 0.073701075761 400.707261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000052.0 1.288808718623E12 +1.006 0.00363 1.52590219E-4 7.62951095E-4 0.073701075761 400.707261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000010.0 1.288808718723E12 +1.006 0.00458 1.52590219E-4 7.62951095E-4 0.073395895323 400.871071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000065.0 1.288808718823E12 +1.006 0.00559 1.52590219E-4 7.62951095E-4 0.073395895323 400.871071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000029.0 1.288808718923E12 +1.006 0.00659 1.52590219E-4 7.62951095E-4 0.073701075761 400.871071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000051.0 1.288808719023E12 +1.006 0.00765 1.52590219E-4 7.62951095E-4 0.073701075761 401.023452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000056.0 1.288808719123E12 +1.006 0.0086 1.52590219E-4 7.62951095E-4 0.073701075761 401.023452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000032.0 1.288808719223E12 +1.006 0.00963 1.52590219E-4 7.62951095E-4 0.073701075761 401.023452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000054.0 1.288808719323E12 +1.006 0.01059 1.52590219E-4 7.62951095E-4 0.073395895323 401.023452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000043.0 1.288808719423E12 +1.006 0.01165 1.52590219E-4 7.62951095E-4 0.073395895323 401.175833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000031.0 1.288808719523E12 +1.006 0.01259 1.52590219E-4 7.62951095E-4 0.073701075761 401.175833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000030.0 1.288808719623E12 +1.006 0.01363 1.52590219E-4 7.62951095E-4 0.073701075761 401.175833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000147.0 1.288808719723E12 +1.006 0.0146 1.52590219E-4 7.62951095E-4 0.073395895323 401.332023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999959.0 1.288808719823E12 +1.006 0.01568 1.52590219E-4 7.62951095E-4 0.073395895323 401.332023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000012.0 1.288808719923E12 +1.006 0.0166 1.52590219E-4 7.62951095E-4 0.073701075761 401.332023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000077.0 1.288808720023E12 +1.006 0.01765 1.52590219E-4 7.62951095E-4 0.073701075761 401.480595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000002.0 1.288808720123E12 +1.006 0.01862 1.52590219E-4 7.62951095E-4 0.073701075761 401.480595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000080.0 1.288808720223E12 +1.006 0.01968 1.52590219E-4 7.62951095E-4 0.073701075761 401.480595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000006.0 1.288808720323E12 +1.006 0.0206 1.52590219E-4 7.62951095E-4 0.073701075761 401.632976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000077.0 1.288808720423E12 +1.006 0.02162 1.52590219E-4 7.62951095E-4 0.073701075761 401.632976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000020.0 1.288808720523E12 +1.006 0.0226 1.52590219E-4 7.62951095E-4 0.073701075761 401.632976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000065.0 1.288808720623E12 +1.006 0.02366 1.52590219E-4 7.62951095E-4 0.073701075761 401.796785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000014.0 1.288808720723E12 +1.006 0.02459 1.52590219E-4 7.62951095E-4 0.073701075761 401.796785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000066.0 1.288808720823E12 +1.006 0.02562 1.52590219E-4 7.62951095E-4 0.073701075761 401.796785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000028.0 1.288808720923E12 +1.006 0.0266 1.52590219E-4 7.62951095E-4 0.073701075761 401.796785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000033.0 1.288808721023E12 +1.006 0.02768 1.52590219E-4 7.62951095E-4 0.073701075761 401.956785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000055.0 1.288808721123E12 +1.006 0.02861 1.52590219E-4 7.62951095E-4 0.073701075761 401.956785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000029.0 1.288808721223E12 +1.006 0.02965 1.52590219E-4 7.62951095E-4 0.073701075761 401.956785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000064.0 1.288808721323E12 +1.006 0.0306 1.52590219E-4 7.62951095E-4 0.073701075761 402.120595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000027.0 1.288808721423E12 +1.006 0.03165 1.52590219E-4 7.62951095E-4 0.073701075761 402.120595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000059.0 1.288808721523E12 +1.006 0.03258 1.52590219E-4 7.62951095E-4 0.073701075761 402.120595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000054.0 1.288808721623E12 +1.006 0.0336 1.52590219E-4 7.62951095E-4 0.073701075761 402.185357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000205.0 1.288808721723E12 +1.006 0.03459 1.52590219E-4 7.62951095E-4 0.073701075761 402.185357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999890.0 1.288808721823E12 +1.006 0.03565 1.52590219E-4 7.62951095E-4 0.073701075761 402.185357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000018.0 1.288808721923E12 +1.006 0.03658 1.52590219E-4 7.62951095E-4 0.073701075761 402.173928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000054.0 1.288808722023E12 +1.006 0.0376 1.52590219E-4 7.62951095E-4 0.073701075761 402.173928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000044.0 1.288808722123E12 +1.006 0.03859 1.52590219E-4 7.62951095E-4 0.073701075761 402.173928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000055.0 1.288808722223E12 +1.006 0.03963 1.52590219E-4 7.62951095E-4 0.073701075761 402.1625 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000172.0 1.288808722323E12 +1.006 0.04057 1.52590219E-4 7.62951095E-4 0.073701075761 402.1625 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999898.0 1.288808722423E12 +1.006 0.04154 1.52590219E-4 7.62951095E-4 0.073701075761 402.1625 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000030.0 1.288808722523E12 +1.006 0.04257 1.52590219E-4 7.62951095E-4 0.073701075761 402.1625 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000070.0 1.288808722623E12 +1.006 0.04358 1.52590219E-4 7.62951095E-4 0.073701075761 402.151071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000032.0 1.288808722723E12 +1.006 0.04455 1.52590219E-4 7.62951095E-4 0.073701075761 402.151071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000048.0 1.288808722823E12 +1.006 0.04555 1.52590219E-4 7.62951095E-4 0.073701075761 402.151071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000045.0 1.288808722923E12 +1.006 0.04658 1.52590219E-4 7.62951095E-4 0.073701075761 402.139642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000019.0 1.288808723023E12 +1.006 0.04761 1.52590219E-4 7.62951095E-4 0.073701075761 402.139642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000056.0 1.288808723123E12 +1.006 0.04858 1.52590219E-4 7.62951095E-4 0.073701075761 402.139642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000039.0 1.288808723223E12 +1.006 0.04957 1.52590219E-4 7.62951095E-4 0.073701075761 402.124404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000060.0 1.288808723323E12 +1.006 0.05057 1.52590219E-4 7.62951095E-4 0.073701075761 402.124404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000012.0 1.288808723423E12 +1.006 0.05157 1.52590219E-4 7.62951095E-4 0.073701075761 402.124404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000049.0 1.288808723523E12 +1.006 0.05254 1.52590219E-4 7.62951095E-4 0.073701075761 402.112976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000038.0 1.288808723623E12 +1.006 0.05353 1.52590219E-4 7.62951095E-4 0.073701075761 402.112976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000056.0 1.288808723723E12 +1.006 0.05456 1.52590219E-4 7.62951095E-4 0.073701075761 402.112976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000065.0 1.288808723823E12 +1.006 0.05558 1.52590219E-4 7.62951095E-4 0.073701075761 402.097738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000015.0 1.288808723923E12 +1.006 0.05656 1.52590219E-4 7.62951095E-4 0.073701075761 402.097738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000060.0 1.288808724023E12 +1.006 0.05755 1.52590219E-4 7.62951095E-4 0.073701075761 402.097738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000025.0 1.288808724123E12 +1.006 0.05857 1.52590219E-4 7.62951095E-4 0.073701075761 402.097738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000071.0 1.288808724223E12 +1.006 0.05958 1.52590219E-4 7.62951095E-4 0.073701075761 402.08630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999996.0 1.288808724323E12 +1.006 0.06053 1.52590219E-4 7.62951095E-4 0.073701075761 402.08630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000087.0 1.288808724423E12 +1.006 0.06149 1.52590219E-4 7.62951095E-4 0.073701075761 402.08630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000036.0 1.288808724523E12 +1.006 0.06251 1.52590219E-4 7.62951095E-4 0.073395895323 402.074880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000059.0 1.288808724623E12 +1.006 0.0635 1.52590219E-4 7.62951095E-4 0.073395895323 402.074880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000008.0 1.288808724723E12 +1.006 0.06448 1.52590219E-4 7.62951095E-4 0.073701075761 402.074880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000055.0 1.288808724823E12 +1.006 0.06547 1.52590219E-4 7.62951095E-4 0.073701075761 402.063452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000035.0 1.288808724923E12 +1.006 0.0665 1.52590219E-4 7.62951095E-4 0.073701075761 402.063452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000041.0 1.288808725023E12 +1.006 0.06751 1.52590219E-4 7.62951095E-4 0.073701075761 402.063452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000077.0 1.288808725123E12 +1.006 0.06851 1.52590219E-4 7.62951095E-4 0.073701075761 402.052023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000017.0 1.288808725223E12 +1.006 0.06949 1.52590219E-4 7.62951095E-4 0.073701075761 402.052023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000056.0 1.288808725323E12 +1.006 0.07051 1.52590219E-4 7.62951095E-4 0.073701075761 402.052023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000016.0 1.288808725423E12 +1.006 0.0715 1.52590219E-4 7.62951095E-4 0.073701075761 402.036785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000090.0 1.288808725523E12 +1.006 0.0725 1.52590219E-4 7.62951095E-4 0.073701075761 402.036785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000024.0 1.288808725623E12 +1.006 0.07348 1.52590219E-4 7.62951095E-4 0.073701075761 402.036785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000046.0 1.288808725723E12 +1.006 0.07451 1.52590219E-4 7.62951095E-4 0.073395895323 402.036785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000034.0 1.288808725823E12 +1.006 0.07553 1.52590219E-4 7.62951095E-4 0.073395895323 402.025357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000026.0 1.288808725923E12 +1.006 0.07651 1.52590219E-4 7.62951095E-4 0.073701075761 402.025357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000057.0 1.288808726023E12 +1.006 0.07749 1.52590219E-4 7.62951095E-4 0.073701075761 402.025357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000010.0 1.288808726123E12 +1.006 0.07852 1.52590219E-4 7.62951095E-4 0.073395895323 402.017738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000087.0 1.288808726223E12 +1.006 0.07951 1.52590219E-4 7.62951095E-4 0.073395895323 402.017738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000118.0 1.288808726323E12 +1.006 0.08049 1.52590219E-4 7.62951095E-4 0.073701075761 402.017738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999944.0 1.288808726423E12 +1.006 0.08144 1.52590219E-4 7.62951095E-4 0.073701075761 402.00630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000046.0 1.288808726523E12 +1.006 0.08246 1.52590219E-4 7.62951095E-4 0.073701075761 402.00630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000063.0 1.288808726623E12 +1.006 0.08346 1.52590219E-4 7.62951095E-4 0.073701075761 402.00630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000029.0 1.288808726723E12 +1.006 0.08442 1.52590219E-4 7.62951095E-4 0.073701075761 401.998690476191 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000044.0 1.288808726823E12 +1.006 0.08541 1.52590219E-4 7.62951095E-4 0.073701075761 401.998690476191 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000006.0 1.288808726923E12 +1.006 0.08644 1.52590219E-4 7.62951095E-4 0.073701075761 401.998690476191 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000036.0 1.288808727023E12 +1.006 0.08746 1.52590219E-4 7.62951095E-4 0.073701075761 401.987261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000057.0 1.288808727123E12 +1.006 0.08845 1.52590219E-4 7.62951095E-4 0.073395895323 401.987261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000032.0 1.288808727223E12 +1.006 0.08944 1.52590219E-4 7.62951095E-4 0.073395895323 401.987261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000077.0 1.288808727323E12 +1.006 0.09045 1.52590219E-4 7.62951095E-4 0.073395895323 401.987261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000033.0 1.288808727423E12 +1.006 0.09145 1.52590219E-4 7.62951095E-4 0.073395895323 401.979642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000056.0 1.288808727523E12 +1.006 0.09243 1.52590219E-4 7.62951095E-4 0.073395895323 401.979642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000033.0 1.288808727623E12 +1.006 0.09343 1.52590219E-4 7.62951095E-4 0.073395895323 401.979642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000057.0 1.288808727723E12 +1.006 0.09446 1.52590219E-4 7.62951095E-4 0.073701075761 401.968214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000039.0 1.288808727823E12 +1.006 0.09547 1.52590219E-4 7.62951095E-4 0.073701075761 401.968214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000005.0 1.288808727924E12 +1.006 0.09646 1.52590219E-4 7.62951095E-4 0.073395895323 401.968214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000071.0 1.288808728024E12 +1.006 0.09745 1.52590219E-4 7.62951095E-4 0.073395895323 401.956785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000044.0 1.288808728124E12 +1.006 0.09849 1.52590219E-4 7.62951095E-4 0.073701075761 401.956785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000051.0 1.288808728224E12 +1.006 0.09948 1.52590219E-4 7.62951095E-4 0.073701075761 401.956785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000129.0 1.288808728324E12 +1.008 0.00154 1.52590219E-4 7.62951095E-4 0.073395895323 401.850119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000051.0 1.288808731538E12 +1.008 0.00257 1.52590219E-4 7.62951095E-4 0.073395895323 401.83869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000041.0 1.288808731638E12 +1.008 0.00358 1.52590219E-4 7.62951095E-4 0.073395895323 401.83869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000078.0 1.288808731738E12 +1.008 0.00456 1.52590219E-4 7.62951095E-4 0.073701075761 401.83869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000003.0 1.288808731838E12 +1.008 0.00555 1.52590219E-4 7.62951095E-4 0.073701075761 401.827261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000087.0 1.288808731938E12 +1.008 0.00658 1.52590219E-4 7.62951095E-4 0.073701075761 401.827261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000005.0 1.288808732038E12 +1.008 0.00762 1.52590219E-4 7.62951095E-4 0.073701075761 401.827261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000044.0 1.288808732138E12 +1.008 0.00858 1.52590219E-4 7.62951095E-4 0.073701075761 401.812023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000079.0 1.288808732238E12 +1.008 0.0096 1.52590219E-4 7.62951095E-4 0.073701075761 401.812023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000032.0 1.288808732338E12 +1.008 0.01058 1.52590219E-4 7.62951095E-4 0.073701075761 401.812023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000045.0 1.288808732438E12 +1.008 0.01161 1.52590219E-4 7.62951095E-4 0.073701075761 401.800595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000133.0 1.288808732538E12 +1.008 0.01258 1.52590219E-4 7.62951095E-4 0.073701075761 401.800595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999941.0 1.288808732638E12 +1.008 0.01358 1.52590219E-4 7.62951095E-4 0.073701075761 401.800595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000057.0 1.288808732738E12 +1.008 0.01458 1.52590219E-4 7.62951095E-4 0.073701075761 401.800595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000031.0 1.288808732838E12 +1.008 0.01563 1.52590219E-4 7.62951095E-4 0.073701075761 401.789166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000044.0 1.288808732938E12 +1.008 0.01658 1.52590219E-4 7.62951095E-4 0.073395895323 401.789166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000014.0 1.288808733038E12 +1.008 0.0176 1.52590219E-4 7.62951095E-4 0.073395895323 401.789166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000055.0 1.288808733138E12 +1.008 0.0186 1.52590219E-4 7.62951095E-4 0.073701075761 401.773928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000045.0 1.288808733238E12 +1.008 0.01965 1.52590219E-4 7.62951095E-4 0.073701075761 401.773928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000090.0 1.288808733338E12 +1.008 0.02058 1.52590219E-4 7.62951095E-4 0.073701075761 401.773928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999977.0 1.288808733438E12 +1.008 0.02157 1.52590219E-4 7.62951095E-4 0.073701075761 401.7625 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000055.0 1.288808733538E12 +1.008 0.02258 1.52590219E-4 7.62951095E-4 0.073701075761 401.7625 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000040.0 1.288808733638E12 +1.008 0.02359 1.52590219E-4 7.62951095E-4 0.073701075761 401.7625 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000081.0 1.288808733738E12 +1.008 0.02456 1.52590219E-4 7.62951095E-4 0.073395895323 401.751071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999999.0 1.288808733838E12 +1.008 0.02555 1.52590219E-4 7.62951095E-4 0.073395895323 401.751071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000057.0 1.288808733938E12 +1.008 0.02658 1.52590219E-4 7.62951095E-4 0.073395895323 401.751071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000034.0 1.288808734038E12 +1.008 0.02763 1.52590219E-4 7.62951095E-4 0.073395895323 401.751071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000118.0 1.288808734138E12 +1.008 0.02858 1.52590219E-4 7.62951095E-4 0.073701075761 401.739642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000009.0 1.288808734238E12 +1.008 0.0296 1.52590219E-4 7.62951095E-4 0.073701075761 401.739642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999994.0 1.288808734338E12 +1.008 0.03058 1.52590219E-4 7.62951095E-4 0.073395895323 401.739642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000075.0 1.288808734438E12 +1.008 0.03163 1.52590219E-4 7.62951095E-4 0.073395895323 401.728214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000050.0 1.288808734538E12 +1.008 0.03258 1.52590219E-4 7.62951095E-4 0.073395895323 401.728214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000042.0 1.288808734638E12 +1.008 0.03361 1.52590219E-4 7.62951095E-4 0.073395895323 401.728214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000027.0 1.288808734738E12 +1.008 0.03459 1.52590219E-4 7.62951095E-4 0.073395895323 401.720595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000030.0 1.288808734838E12 +1.008 0.03565 1.52590219E-4 7.62951095E-4 0.073395895323 401.720595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000034.0 1.288808734938E12 +1.008 0.03658 1.52590219E-4 7.62951095E-4 0.073395895323 401.720595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000058.0 1.288808735038E12 +1.008 0.03759 1.52590219E-4 7.62951095E-4 0.073395895323 401.709166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000037.0 1.288808735138E12 +1.008 0.03859 1.52590219E-4 7.62951095E-4 0.073701075761 401.709166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000043.0 1.288808735238E12 +1.008 0.03963 1.52590219E-4 7.62951095E-4 0.073701075761 401.709166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000103.0 1.288808735338E12 +1.008 0.04057 1.52590219E-4 7.62951095E-4 0.073701075761 401.701547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999992.0 1.288808735438E12 +1.008 0.04154 1.52590219E-4 7.62951095E-4 0.073701075761 401.701547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000060.0 1.288808735538E12 +1.008 0.04257 1.52590219E-4 7.62951095E-4 0.073701075761 401.701547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000018.0 1.288808735638E12 +1.008 0.04356 1.52590219E-4 7.62951095E-4 0.073701075761 401.701547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000047.0 1.288808735738E12 +1.008 0.04453 1.52590219E-4 7.62951095E-4 0.073701075761 401.693928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000034.0 1.288808735838E12 +1.008 0.0455 1.52590219E-4 7.62951095E-4 0.073701075761 401.693928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000038.0 1.288808735938E12 +1.008 0.04653 1.52590219E-4 7.62951095E-4 0.073701075761 401.693928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000067.0 1.288808736038E12 +1.008 0.04754 1.52590219E-4 7.62951095E-4 0.073701075761 401.6825 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000031.0 1.288808736138E12 +1.008 0.04854 1.52590219E-4 7.62951095E-4 0.073701075761 401.6825 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000077.0 1.288808736238E12 +1.008 0.04952 1.52590219E-4 7.62951095E-4 0.073701075761 401.6825 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000041.0 1.288808736338E12 +1.008 0.05054 1.52590219E-4 7.62951095E-4 0.073701075761 401.674880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000039.0 1.288808736438E12 +1.008 0.05154 1.52590219E-4 7.62951095E-4 0.073701075761 401.674880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000045.0 1.288808736538E12 +1.008 0.05253 1.52590219E-4 7.62951095E-4 0.073701075761 401.674880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000014.0 1.288808736638E12 +1.008 0.05351 1.52590219E-4 7.62951095E-4 0.073701075761 401.663452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000055.0 1.288808736738E12 +1.008 0.05454 1.52590219E-4 7.62951095E-4 0.073395895323 401.663452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000035.0 1.288808736838E12 +1.008 0.05555 1.52590219E-4 7.62951095E-4 0.073395895323 401.663452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000044.0 1.288808736938E12 +1.008 0.05653 1.52590219E-4 7.62951095E-4 0.073395895323 401.655833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000038.0 1.288808737038E12 +1.008 0.0575 1.52590219E-4 7.62951095E-4 0.073395895323 401.655833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000040.0 1.288808737138E12 +1.008 0.05854 1.52590219E-4 7.62951095E-4 0.073395895323 401.655833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000069.0 1.288808737238E12 +1.008 0.05954 1.52590219E-4 7.62951095E-4 0.073395895323 401.648214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000155.0 1.288808737338E12 +1.008 0.06051 1.52590219E-4 7.62951095E-4 0.073701075761 401.648214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999911.0 1.288808737438E12 +1.008 0.06147 1.52590219E-4 7.62951095E-4 0.073701075761 401.648214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000046.0 1.288808737538E12 +1.008 0.0625 1.52590219E-4 7.62951095E-4 0.073395895323 401.648214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000040.0 1.288808737638E12 +1.008 0.06347 1.52590219E-4 7.62951095E-4 0.073395895323 401.636785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000072.0 1.288808737738E12 +1.008 0.06445 1.52590219E-4 7.62951095E-4 0.073701075761 401.636785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000015.0 1.288808737838E12 +1.008 0.06544 1.52590219E-4 7.62951095E-4 0.073701075761 401.636785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000029.0 1.288808737938E12 +1.008 0.06647 1.52590219E-4 7.62951095E-4 0.073395895323 401.625357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000058.0 1.288808738038E12 +1.008 0.06746 1.52590219E-4 7.62951095E-4 0.073395895323 401.625357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000055.0 1.288808738138E12 +1.008 0.06846 1.52590219E-4 7.62951095E-4 0.073395895323 401.625357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000071.0 1.288808738238E12 +1.008 0.06943 1.52590219E-4 7.62951095E-4 0.073395895323 401.613928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000008.0 1.288808738338E12 +1.008 0.07045 1.52590219E-4 7.62951095E-4 0.073395895323 401.613928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000075.0 1.288808738438E12 +1.008 0.07144 1.52590219E-4 7.62951095E-4 0.073395895323 401.613928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000035.0 1.288808738538E12 +1.008 0.07243 1.52590219E-4 7.62951095E-4 0.073395895323 401.6025 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000026.0 1.288808738638E12 +1.008 0.07343 1.52590219E-4 7.62951095E-4 0.073395895323 401.6025 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000035.0 1.288808738738E12 +1.008 0.07446 1.52590219E-4 7.62951095E-4 0.073395895323 401.6025 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000041.0 1.288808738838E12 +1.008 0.07547 1.52590219E-4 7.62951095E-4 0.073395895323 401.587261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000199.0 1.288808738938E12 +1.008 0.07645 1.52590219E-4 7.62951095E-4 0.073395895323 401.587261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999882.0 1.288808739038E12 +1.008 0.07743 1.52590219E-4 7.62951095E-4 0.073395895323 401.587261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000069.0 1.288808739138E12 +1.008 0.07847 1.52590219E-4 7.62951095E-4 0.073701075761 401.587261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000019.0 1.288808739238E12 +1.008 0.07946 1.52590219E-4 7.62951095E-4 0.073701075761 401.575833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000065.0 1.288808739338E12 +1.008 0.08042 1.52590219E-4 7.62951095E-4 0.073395895323 401.575833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000021.0 1.288808739438E12 +1.008 0.0814 1.52590219E-4 7.62951095E-4 0.073395895323 401.575833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000062.0 1.288808739538E12 +1.008 0.08241 1.52590219E-4 7.62951095E-4 0.073395895323 401.564404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000022.0 1.288808739638E12 +1.008 0.08342 1.52590219E-4 7.62951095E-4 0.073395895323 401.564404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000055.0 1.288808739738E12 +1.008 0.0844 1.52590219E-4 7.62951095E-4 0.073395895323 401.564404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000041.0 1.288808739838E12 +1.008 0.0854 1.52590219E-4 7.62951095E-4 0.073395895323 401.552976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000037.0 1.288808739938E12 +1.008 0.08642 1.52590219E-4 7.62951095E-4 0.073701075761 401.552976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000076.0 1.288808740038E12 +1.008 0.08743 1.52590219E-4 7.62951095E-4 0.073701075761 401.552976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000043.0 1.288808740138E12 +1.008 0.08842 1.52590219E-4 7.62951095E-4 0.073395895323 401.537738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000048.0 1.288808740238E12 +1.008 0.0894 1.52590219E-4 7.62951095E-4 0.073395895323 401.537738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000025.0 1.288808740338E12 +1.008 0.0904 1.52590219E-4 7.62951095E-4 0.073395895323 401.537738095238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000060.0 1.288808740438E12 +1.008 0.09141 1.52590219E-4 7.62951095E-4 0.073395895323 401.52630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000138.0 1.288808740538E12 +1.008 0.09238 1.52590219E-4 7.62951095E-4 0.073395895323 401.52630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999916.0 1.288808740638E12 +1.008 0.09339 1.52590219E-4 7.62951095E-4 0.073395895323 401.52630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999997.0 1.288808740738E12 +1.008 0.09442 1.52590219E-4 7.62951095E-4 0.073395895323 401.52630952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000073.0 1.288808740838E12 +1.008 0.09544 1.52590219E-4 7.62951095E-4 0.073395895323 401.514880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000067.0 1.288808740938E12 +1.008 0.09641 1.52590219E-4 7.62951095E-4 0.073395895323 401.514880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000033.0 1.288808741038E12 +1.008 0.09741 1.52590219E-4 7.62951095E-4 0.073395895323 401.514880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000044.0 1.288808741138E12 +1.008 0.09844 1.52590219E-4 7.62951095E-4 0.073395895323 401.503452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000046.0 1.288808741238E12 +1.008 0.09944 1.52590219E-4 7.62951095E-4 0.073395895323 401.503452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000085.0 1.288808741338E12 +1.01 0.00105 1.52590219E-4 7.62951095E-4 0.073701075761 401.400595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999984.0 1.288808744552E12 +1.01 0.00213 1.52590219E-4 7.62951095E-4 0.073701075761 401.400595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000082.0 1.288808744652E12 +1.01 0.00307 1.52590219E-4 7.62951095E-4 0.073701075761 401.392976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000100.0 1.288808744752E12 +1.01 0.00414 1.52590219E-4 7.62951095E-4 0.073701075761 401.392976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999957.0 1.288808744852E12 +1.01 0.00504 1.52590219E-4 7.62951095E-4 0.073701075761 401.392976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000039.0 1.288808744952E12 +1.01 0.00613 1.52590219E-4 7.62951095E-4 0.073701075761 401.381547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000101.0 1.288808745052E12 +1.01 0.00707 1.52590219E-4 7.62951095E-4 0.073701075761 401.381547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000059.0 1.288808745152E12 +1.01 0.00817 1.52590219E-4 7.62951095E-4 0.073701075761 401.381547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000044.0 1.288808745252E12 +1.01 0.00907 1.52590219E-4 7.62951095E-4 0.073701075761 401.370119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999964.0 1.288808745352E12 +1.01 0.01014 1.52590219E-4 7.62951095E-4 0.073701075761 401.370119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000103.0 1.288808745452E12 +1.01 0.01107 1.52590219E-4 7.62951095E-4 0.073701075761 401.370119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999989.0 1.288808745552E12 +1.01 0.01215 1.52590219E-4 7.62951095E-4 0.073701075761 401.370119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000099.0 1.288808745652E12 +1.01 0.01305 1.52590219E-4 7.62951095E-4 0.073701075761 401.3625 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000141.0 1.288808745752E12 +1.01 0.01414 1.52590219E-4 7.62951095E-4 0.073701075761 401.3625 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999892.0 1.288808745852E12 +1.01 0.01509 1.52590219E-4 7.62951095E-4 0.073701075761 401.3625 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000060.0 1.288808745952E12 +1.01 0.01618 1.52590219E-4 7.62951095E-4 0.073701075761 401.351071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000057.0 1.288808746052E12 +1.01 0.01707 1.52590219E-4 7.62951095E-4 0.073701075761 401.351071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999995.0 1.288808746152E12 +1.01 0.01817 1.52590219E-4 7.62951095E-4 0.073701075761 401.351071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000098.0 1.288808746252E12 +1.01 0.01911 1.52590219E-4 7.62951095E-4 0.073701075761 401.339642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999983.0 1.288808746352E12 +1.01 0.02018 1.52590219E-4 7.62951095E-4 0.073701075761 401.339642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000102.0 1.288808746452E12 +1.01 0.02105 1.52590219E-4 7.62951095E-4 0.073701075761 401.339642857143 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000059.0 1.288808746552E12 +1.01 0.02214 1.52590219E-4 7.62951095E-4 0.073701075761 401.328214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000026.0 1.288808746652E12 +1.01 0.02308 1.52590219E-4 7.62951095E-4 0.073701075761 401.328214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000077.0 1.288808746752E12 +1.01 0.02415 1.52590219E-4 7.62951095E-4 0.073701075761 401.328214285714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999953.0 1.288808746852E12 +1.01 0.02506 1.52590219E-4 7.62951095E-4 0.073701075761 401.316785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000059.0 1.288808746952E12 +1.01 0.02616 1.52590219E-4 7.62951095E-4 0.073701075761 401.316785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000069.0 1.288808747052E12 +1.01 0.0271 1.52590219E-4 7.62951095E-4 0.073701075761 401.316785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999995.0 1.288808747152E12 +1.01 0.02818 1.52590219E-4 7.62951095E-4 0.073701075761 401.316785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000147.0 1.288808747252E12 +1.01 0.02909 1.52590219E-4 7.62951095E-4 0.073701075761 401.301547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999933.0 1.288808747352E12 +1.01 0.03017 1.52590219E-4 7.62951095E-4 0.073701075761 401.301547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000087.0 1.288808747452E12 +1.01 0.0311 1.52590219E-4 7.62951095E-4 0.073701075761 401.301547619048 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999997.0 1.288808747552E12 +1.01 0.03218 1.52590219E-4 7.62951095E-4 0.073701075761 401.290119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000105.0 1.288808747652E12 +1.01 0.03308 1.52590219E-4 7.62951095E-4 0.073701075761 401.290119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000176.0 1.288808747752E12 +1.01 0.03417 1.52590219E-4 7.62951095E-4 0.073701075761 401.290119047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999844.0 1.288808747852E12 +1.01 0.03511 1.52590219E-4 7.62951095E-4 0.073701075761 401.27869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000039.0 1.288808747952E12 +1.01 0.03618 1.52590219E-4 7.62951095E-4 0.073701075761 401.27869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000099.0 1.288808748052E12 +1.01 0.03708 1.52590219E-4 7.62951095E-4 0.073701075761 401.27869047619 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999985.0 1.288808748152E12 +1.01 0.03818 1.52590219E-4 7.62951095E-4 0.073701075761 401.267261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000173.0 1.288808748252E12 +1.01 0.03912 1.52590219E-4 7.62951095E-4 0.073701075761 401.267261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999915.0 1.288808748352E12 +1.01 0.04017 1.52590219E-4 7.62951095E-4 0.073701075761 401.267261904762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000085.0 1.288808748452E12 +1.01 0.04105 1.52590219E-4 7.62951095E-4 0.073701075761 401.252023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999991.0 1.288808748552E12 +1.01 0.04212 1.52590219E-4 7.62951095E-4 0.073701075761 401.252023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000092.0 1.288808748652E12 +1.01 0.04305 1.52590219E-4 7.62951095E-4 0.073701075761 401.252023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000030.0 1.288808748752E12 +1.01 0.04411 1.52590219E-4 7.62951095E-4 0.073395895323 401.252023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000093.0 1.288808748852E12 +1.01 0.045 1.52590219E-4 7.62951095E-4 0.073395895323 401.240595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999982.0 1.288808748952E12 +1.01 0.0461 1.52590219E-4 7.62951095E-4 0.073701075761 401.240595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000066.0 1.288808749052E12 +1.01 0.04705 1.52590219E-4 7.62951095E-4 0.073701075761 401.240595238095 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000074.0 1.288808749152E12 +1.01 0.04814 1.52590219E-4 7.62951095E-4 0.073701075761 401.229166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000070.0 1.288808749252E12 +1.01 0.04904 1.52590219E-4 7.62951095E-4 0.073701075761 401.229166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999927.0 1.288808749352E12 +1.01 0.05011 1.52590219E-4 7.62951095E-4 0.073701075761 401.229166666667 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000109.0 1.288808749452E12 +1.01 0.05104 1.52590219E-4 7.62951095E-4 0.073701075761 401.213928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000010.0 1.288808749552E12 +1.01 0.05211 1.52590219E-4 7.62951095E-4 0.073701075761 401.213928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000059.0 1.288808749652E12 +1.01 0.053 1.52590219E-4 7.62951095E-4 0.073701075761 401.213928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000057.0 1.288808749752E12 +1.01 0.05409 1.52590219E-4 7.62951095E-4 0.073701075761 401.2025 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999989.0 1.288808749852E12 +1.01 0.05502 1.52590219E-4 7.62951095E-4 0.073701075761 401.2025 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000041.0 1.288808749952E12 +1.01 0.05611 1.52590219E-4 7.62951095E-4 0.073701075761 401.2025 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000091.0 1.288808750052E12 +1.01 0.05699 1.52590219E-4 7.62951095E-4 0.073701075761 401.191071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999984.0 1.288808750152E12 +1.01 0.0581 1.52590219E-4 7.62951095E-4 0.073701075761 401.191071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000105.0 1.288808750252E12 +1.01 0.05903 1.52590219E-4 7.62951095E-4 0.073701075761 401.191071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999989.0 1.288808750352E12 +1.01 0.0601 1.52590219E-4 7.62951095E-4 0.073701075761 401.191071428571 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000086.0 1.288808750452E12 +1.01 0.06098 1.52590219E-4 7.62951095E-4 0.073701075761 401.183452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999984.0 1.288808750552E12 +1.01 0.06204 1.52590219E-4 7.62951095E-4 0.073701075761 401.183452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000167.0 1.288808750652E12 +1.01 0.06299 1.52590219E-4 7.62951095E-4 0.073701075761 401.183452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999923.0 1.288808750752E12 +1.01 0.06404 1.52590219E-4 7.62951095E-4 0.073701075761 401.172023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000111.0 1.288808750852E12 +1.01 0.06495 1.52590219E-4 7.62951095E-4 0.073701075761 401.172023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999987.0 1.288808750952E12 +1.01 0.06598 1.52590219E-4 7.62951095E-4 0.073701075761 401.172023809524 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000059.0 1.288808751052E12 +1.01 0.06697 1.52590219E-4 7.62951095E-4 0.073701075761 401.164404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999996.0 1.288808751152E12 +1.01 0.06802 1.52590219E-4 7.62951095E-4 0.073701075761 401.164404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000101.0 1.288808751252E12 +1.01 0.06896 1.52590219E-4 7.62951095E-4 0.073701075761 401.164404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000001.0 1.288808751352E12 +1.01 0.06999 1.52590219E-4 7.62951095E-4 0.073701075761 401.152976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000087.0 1.288808751452E12 +1.01 0.07097 1.52590219E-4 7.62951095E-4 0.073701075761 401.152976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999988.0 1.288808751552E12 +1.01 0.072 1.52590219E-4 7.62951095E-4 0.073701075761 401.152976190476 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000092.0 1.288808751652E12 +1.01 0.07294 1.52590219E-4 7.62951095E-4 0.073701075761 401.145357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999983.0 1.288808751752E12 +1.01 0.07397 1.52590219E-4 7.62951095E-4 0.073701075761 401.145357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000059.0 1.288808751852E12 +1.01 0.07496 1.52590219E-4 7.62951095E-4 0.073701075761 401.145357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000029.0 1.288808751952E12 +1.01 0.076 1.52590219E-4 7.62951095E-4 0.073701075761 401.145357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000092.0 1.288808752052E12 +1.01 0.07693 1.52590219E-4 7.62951095E-4 0.073701075761 401.133928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000003.0 1.288808752152E12 +1.01 0.07798 1.52590219E-4 7.62951095E-4 0.073701075761 401.133928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000093.0 1.288808752252E12 +1.01 0.07896 1.52590219E-4 7.62951095E-4 0.073701075761 401.133928571429 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000006.0 1.288808752352E12 +1.01 0.07999 1.52590219E-4 7.62951095E-4 0.073701075761 401.1225 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000078.0 1.288808752452E12 +1.01 0.08091 1.52590219E-4 7.62951095E-4 0.073701075761 401.1225 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000001.0 1.288808752552E12 +1.01 0.08193 1.52590219E-4 7.62951095E-4 0.073701075761 401.1225 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000087.0 1.288808752652E12 +1.01 0.08293 1.52590219E-4 7.62951095E-4 0.073701075761 401.114880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000011.0 1.288808752752E12 +1.01 0.08394 1.52590219E-4 7.62951095E-4 0.073701075761 401.114880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000051.0 1.288808752852E12 +1.01 0.08489 1.52590219E-4 7.62951095E-4 0.073701075761 401.114880952381 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000077.0 1.288808752952E12 +1.01 0.08592 1.52590219E-4 7.62951095E-4 0.073701075761 401.103452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000056.0 1.288808753052E12 +1.01 0.08692 1.52590219E-4 7.62951095E-4 0.073701075761 401.103452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000050.0 1.288808753152E12 +1.01 0.08796 1.52590219E-4 7.62951095E-4 0.073701075761 401.103452380952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000089.0 1.288808753252E12 +1.01 0.08891 1.52590219E-4 7.62951095E-4 0.073701075761 401.095833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999940.0 1.288808753352E12 +1.01 0.08993 1.52590219E-4 7.62951095E-4 0.073701075761 401.095833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000078.0 1.288808753452E12 +1.01 0.09092 1.52590219E-4 7.62951095E-4 0.073701075761 401.095833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999998.0 1.288808753552E12 +1.01 0.09194 1.52590219E-4 7.62951095E-4 0.073701075761 401.095833333333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000090.0 1.288808753652E12 +1.01 0.0929 1.52590219E-4 7.62951095E-4 0.073701075761 401.084404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999984.0 1.288808753752E12 +1.01 0.09393 1.52590219E-4 7.62951095E-4 0.073701075761 401.084404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000057.0 1.288808753852E12 +1.01 0.09494 1.52590219E-4 7.62951095E-4 0.073701075761 401.084404761905 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000029.0 1.288808753952E12 +1.01 0.09596 1.52590219E-4 7.62951095E-4 0.073701075761 401.076785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000096.0 1.288808754052E12 +1.01 0.0969 1.52590219E-4 7.62951095E-4 0.073701075761 401.076785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999993.0 1.288808754152E12 +1.01 0.09795 1.52590219E-4 7.62951095E-4 0.073701075761 401.076785714286 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000108.0 1.288808754252E12 +1.01 0.09895 1.52590219E-4 7.62951095E-4 0.073701075761 401.065357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 999983.0 1.288808754352E12 +1.01 0.09996 1.52590219E-4 7.62951095E-4 0.073701075761 401.065357142857 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000082.0 1.288808754452E12 diff --git a/ch.psi.fda/src/test/resources/testdata/text/textdata2.txt b/ch.psi.fda/src/test/resources/testdata/text/textdata2.txt new file mode 100644 index 0000000..6aee773 --- /dev/null +++ b/ch.psi.fda/src/test/resources/testdata/text/textdata2.txt @@ -0,0 +1,19 @@ +#id0 idD0 idD1 timestamp +#0 0 0 0 +0.0 0.0 0.0010 1.283931433121E12 +0.5 100.0 0.496 1.283931433703E12 +1.0 201.0 1.0035 1.283931434403E12 +1.5 301.0 1.5005 1.283931434999E12 +2.0 401.0 2.005 1.283931435598E12 +2.5 500.0 2.496 1.283931436197E12 +3.0 601.0 3.0045 1.283931436866E12 +3.5 701.0 3.5015 1.283931437463E12 +4.0 801.0 4.0045 1.283931438063E12 +4.5 901.0 4.5025 1.283931438659E12 +5.0 1000.0 5.002 1.283931439556E12 +5.5 1100.0 5.4975000000000005 1.283931440155E12 +6.0 1201.0 6.0045 1.283931440822E12 +6.5 1301.0 6.5035 1.283931441404E12 +7.0 1401.0 7.0045 1.283931442002E12 +7.5 1501.0 7.505 1.283931442599E12 +8.0 1600.0 8.0 1.283931443198E12 diff --git a/ch.psi.fda/src/test/resources/testdata/text/textdata3-2d.txt b/ch.psi.fda/src/test/resources/testdata/text/textdata3-2d.txt new file mode 100644 index 0000000..7e3de29 --- /dev/null +++ b/ch.psi.fda/src/test/resources/testdata/text/textdata3-2d.txt @@ -0,0 +1,121 @@ +#id2 id0 idD0 idD1 +#1 0 0 0 +1.0 0.0 0.0 5.0E-4 +1.0 0.5 100.0 0.496 +1.0 1.0 201.0 1.004 +1.0 1.5 301.0 1.5015 +1.0 2.0 400.0 2.0005 +1.0 2.5 500.0 2.496 +1.0 3.0 601.0 3.004 +1.0 3.5 701.0 3.5015 +1.0 4.0 801.0 4.0045 +1.0 4.5 901.0 4.5025 +1.0 5.0 1000.0 5.001 +1.0 5.5 1100.0 5.497 +1.0 6.0 1201.0 6.0040000000000004 +1.0 6.5 1301.0 6.5040000000000004 +1.0 7.0 1401.0 7.0045 +1.0 7.5 1501.0 7.5045 +1.0 8.0 1600.0 8.0015 +0.5 0.0 0.0 0.0010 +0.5 0.5 100.0 0.496 +0.5 1.0 201.0 1.0045 +0.5 1.5 301.0 1.5015 +0.5 2.0 401.0 2.0045 +0.5 2.5 501.0 2.5005 +0.5 3.0 601.0 3.0035000000000003 +0.5 3.5 701.0 3.5015 +0.5 4.0 801.0 4.005 +0.5 4.5 900.0 4.4975000000000005 +0.5 5.0 1000.0 5.0015 +0.5 5.5 1100.0 5.498 +0.5 6.0 1200.0 6.0005 +0.5 6.5 1300.0 6.4985 +0.5 7.0 1401.0 7.0045 +0.5 7.5 1501.0 7.505 +0.5 8.0 1600.0 8.001 +0.0 0.0 0.0 0.0015 +0.0 0.5 100.0 0.4965 +0.0 1.0 201.0 1.004 +0.0 1.5 301.0 1.5015 +0.0 2.0 401.0 2.005 +0.0 2.5 500.0 2.4955000000000003 +0.0 3.0 601.0 3.0035000000000003 +0.0 3.5 701.0 3.5015 +0.0 4.0 801.0 4.0045 +0.0 4.5 901.0 4.503 +0.0 5.0 1000.0 5.0015 +0.0 5.5 1100.0 5.498 +0.0 6.0 1200.0 6.002 +0.0 6.5 1300.0 6.4990000000000006 +0.0 7.0 1400.0 7.001 +0.0 7.5 1500.0 7.5 +0.0 8.0 1600.0 8.0015 +-0.5 0.0 0.0 0.0020 +-0.5 0.5 100.0 0.4965 +-0.5 1.0 201.0 1.004 +-0.5 1.5 301.0 1.5015 +-0.5 2.0 401.0 2.005 +-0.5 2.5 500.0 2.496 +-0.5 3.0 601.0 3.004 +-0.5 3.5 701.0 3.5015 +-0.5 4.0 801.0 4.0045 +-0.5 4.5 901.0 4.5025 +-0.5 5.0 1000.0 5.002 +-0.5 5.5 1100.0 5.4985 +-0.5 6.0 1200.0 6.0025 +-0.5 6.5 1300.0 6.4995 +-0.5 7.0 1400.0 7.0 +-0.5 7.5 1500.0 7.4995 +-0.5 8.0 1600.0 8.0005 +-1.0 0.0 0.0 0.0020 +-1.0 0.5 100.0 0.4965 +-1.0 1.0 201.0 1.004 +-1.0 1.5 301.0 1.5015 +-1.0 2.0 401.0 2.0045 +-1.0 2.5 501.0 2.5005 +-1.0 3.0 601.0 3.004 +-1.0 3.5 701.0 3.5015 +-1.0 4.0 801.0 4.0045 +-1.0 4.5 901.0 4.503 +-1.0 5.0 1000.0 5.0015 +-1.0 5.5 1100.0 5.498 +-1.0 6.0 1200.0 6.0025 +-1.0 6.5 1300.0 6.4990000000000006 +-1.0 7.0 1400.0 7.001 +-1.0 7.5 1500.0 7.4995 +-1.0 8.0 1600.0 8.0005 +-1.5 0.0 0.0 0.0020 +-1.5 0.5 100.0 0.4965 +-1.5 1.0 201.0 1.004 +-1.5 1.5 301.0 1.5015 +-1.5 2.0 401.0 2.005 +-1.5 2.5 500.0 2.4955000000000003 +-1.5 3.0 601.0 3.0035000000000003 +-1.5 3.5 701.0 3.5015 +-1.5 4.0 801.0 4.005 +-1.5 4.5 900.0 4.4975000000000005 +-1.5 5.0 1000.0 5.002 +-1.5 5.5 1100.0 5.498 +-1.5 6.0 1200.0 6.002 +-1.5 6.5 1300.0 6.4995 +-1.5 7.0 1400.0 7.0005 +-1.5 7.5 1500.0 7.4995 +-1.5 8.0 1600.0 8.0005 +-2.0 0.0 0.0 0.0020 +-2.0 0.5 100.0 0.4965 +-2.0 1.0 201.0 1.004 +-2.0 1.5 301.0 1.5015 +-2.0 2.0 401.0 2.005 +-2.0 2.5 500.0 2.4955000000000003 +-2.0 3.0 601.0 3.0045 +-2.0 3.5 701.0 3.5015 +-2.0 4.0 801.0 4.0045 +-2.0 4.5 901.0 4.503 +-2.0 5.0 1000.0 5.0015 +-2.0 5.5 1100.0 5.498 +-2.0 6.0 1200.0 6.002 +-2.0 6.5 1300.0 6.4995 +-2.0 7.0 1400.0 7.0005 +-2.0 7.5 1500.0 7.4995 +-2.0 8.0 1600.0 8.0005 diff --git a/ch.psi.fda/src/test/resources/testdata/text/textdata4-array.txt b/ch.psi.fda/src/test/resources/testdata/text/textdata4-array.txt new file mode 100644 index 0000000..3ced04e --- /dev/null +++ b/ch.psi.fda/src/test/resources/testdata/text/textdata4-array.txt @@ -0,0 +1,19 @@ +#id0 idD0 idD2 idD1 +#0 0 0 0 +0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0015 +0.5 100.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.4985 +1.0 200.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 +1.5 300.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.497 +2.0 401.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2.005 +2.5 500.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2.497 +3.0 601.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 3.005 +3.5 700.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 3.4985 +4.0 800.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 4.001 +4.5 900.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 4.499 +5.0 1000.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 5.002 +5.5 1100.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 5.4995 +6.0 1200.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.002 +6.5 1300.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.5 +7.0 1400.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 7.001 +7.5 1500.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 7.5 +8.0 1600.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 8.0015 diff --git a/ch.psi.fda/src/test/resources/testscripts/testscript1.sh b/ch.psi.fda/src/test/resources/testscripts/testscript1.sh new file mode 100755 index 0000000..e2435ec --- /dev/null +++ b/ch.psi.fda/src/test/resources/testscripts/testscript1.sh @@ -0,0 +1,4 @@ +#!/bin/bash +echo "Test Script" +sleep 4 +exit 0 \ No newline at end of file diff --git a/ch.psi.fda/src/test/resources/testscripts/testscript2-error.sh b/ch.psi.fda/src/test/resources/testscripts/testscript2-error.sh new file mode 100755 index 0000000..08abe94 --- /dev/null +++ b/ch.psi.fda/src/test/resources/testscripts/testscript2-error.sh @@ -0,0 +1,3 @@ +#!/bin/bash +echo "Test Script - Error" +exit 1 \ No newline at end of file