diff --git a/config/.DS_Store b/config/.DS_Store
index c4e2741..954e675 100644
Binary files a/config/.DS_Store and b/config/.DS_Store differ
diff --git a/config/config.properties b/config/config.properties
index 646fbef..4d2ba6d 100755
--- a/config/config.properties
+++ b/config/config.properties
@@ -1,7 +1,7 @@
-#Tue Nov 06 14:31:07 CET 2018
+#Wed Mar 20 13:51:54 CET 2019
autoSaveScanData=true
commandExecutionEvents=false
-createSessionFiles=true
+createSessionFiles=false
dataLayout=default
dataPath={data}/{year}_{month}/{date}/{date}_{time}_{name}
dataProvider=h5
@@ -11,6 +11,7 @@ dataScanReleaseRecords=false
dataServerPort=5573
depthDimension=0
generateCommandExecutionEvents=true
+hideServerMessages=false
hostName=
instanceName=Dev
logDaysToLive=50
@@ -18,8 +19,9 @@ logLevel=Fine
logLevelConsole=Off
logPath={logs}/{date}_{time}
notificationLevel=Off
+saveCommandStatistics=true
scanStreamerPort=5563
-serverEnabled=false
+serverEnabled=true
serverPort=8080
simulation=false
terminalEnabled=false
@@ -28,5 +30,5 @@ userAuthenticator=ch.psi.pshell.security.LdapAuthenticator | ldap\://d.psi.ch |
userManagement=true
versionTrackingEnabled=true
versionTrackingLogin={context}/svcusr-hlapp_robot
-versionTrackingManual=true
+versionTrackingManual=false
versionTrackingRemote=git@git.psi.ch\:pshell_config/dev.git
diff --git a/config/devices.properties b/config/devices.properties
index 990f240..7e7db88 100755
--- a/config/devices.properties
+++ b/config/devices.properties
@@ -1,6 +1,6 @@
dispatcher=ch.psi.pshell.bs.Provider|tcp://localhost:9999|||
-cam_server_local=ch.psi.pshell.bs.PipelineServer|localhost:8889|||true
-cam_server=ch.psi.pshell.bs.PipelineServer|sf-daqsync-01:8889|||true
+#cam_server_local=ch.psi.pshell.bs.PipelineServer|localhost:8889|||true
+cam_server=ch.psi.pshell.bs.PipelineServer|localhost:8889|||true
#ts1=ch.psi.pshell.epics.GenericArray|TESTIOC:TESTWF2:MyWF|||
#monit_cam=ch.psi.pshell.imaging.MjpegSource|http://axis-accc8e9cc87b.psi.ch/axis-cgi/mjpg/video.cgi?camera=2 reopen||-200|false
#tststr=ch.psi.pshell.epics.ChannelString|TESTIOC:TESTSINUS:SinCalc|||
@@ -69,7 +69,7 @@ pv=ch.psi.pshell.epics.ProcessVariable|TESTIOC:TESTCALCOUT:Input|||true
shutter=ch.psi.pshell.epics.BinaryPositioner|TESTIOC:TESTBO:MyBO TESTIOC:TESTBO:MyBO|||true
$motor=ch.psi.pshell.epics.Motor|MTEST-GOBBO:MOT1|||true
$motor2=ch.psi.pshell.epics.Motor|MTEST-GOBBO:MOT2|||true
-table=ch.psi.pshell.device.MotorGroupBase|motor motor2|||
+table=ch.psi.pshell.device.MotorGroupBase|motor motor2|||true
$manip=ch.psi.pshell.epics.Manipulator||||
tab=ch.psi.pshell.device.MotorGroupDiscretePositioner|table|||
#tab2=ch.psi.pshell.device.MotorGroupDiscretePositioner|table|||
diff --git a/config/diffcalc/test1.json b/config/diffcalc/test1.json
new file mode 100644
index 0000000..5407051
--- /dev/null
+++ b/config/diffcalc/test1.json
@@ -0,0 +1,38 @@
+{
+ "name": "test1",
+ "crystal": "['cubic', 'Triclinic', 7.723, 7.707, 7.723, 90.0, 89.265, 90.0]",
+ "reflist": {
+ "1": {
+ "tag": null,
+ "hkl": "[0.0, 1.0, 2.0]",
+ "pos": "[5.0, 9.379, 19.7895, 102.6162, 0, 0]",
+ "energy": 9.5,
+ "time": "2019-02-26T18:02:19.132000"
+ },
+ "2": {
+ "tag": null,
+ "hkl": "[2.0, 0.0, 2.0]",
+ "pos": "[5.0, 19.0754, 20.1865, 11.9693, 0, 0]",
+ "energy": 9.5,
+ "time": "2019-02-26T18:02:19.148000"
+ },
+ "3": {
+ "tag": null,
+ "hkl": "[2.0, 2.0, 2.0]",
+ "pos": "[5.0, 27.1234, 21.2368, 60.0354, 0, 0]",
+ "energy": 9.5,
+ "time": "2019-02-26T18:02:19.213000"
+ }
+ },
+ "orientlist": {},
+ "tau": 0,
+ "sigma": 0,
+ "reference": {
+ "n_hkl_configured": null,
+ "n_phi_configured": "[0.0, 0.0, 1.0]"
+ },
+ "u": null,
+ "ub": null,
+ "or0": null,
+ "or1": null
+}
\ No newline at end of file
diff --git a/config/diffcalc/test2.json b/config/diffcalc/test2.json
new file mode 100644
index 0000000..b945491
--- /dev/null
+++ b/config/diffcalc/test2.json
@@ -0,0 +1,38 @@
+{
+ "name": "test2",
+ "crystal": "['cubic', 'Triclinic', 7.723, 7.707, 7.723, 90.0, 89.265, 90.0]",
+ "reflist": {
+ "1": {
+ "tag": "None",
+ "hkl": "[0.0, 1.0, 2.0]",
+ "pos": "[5.0, 9.379, 19.7895, 102.6162, 0, 0]",
+ "energy": 9.5,
+ "time": "2019-02-15T11:11:34.710000"
+ },
+ "2": {
+ "tag": "None",
+ "hkl": "[2.0, 0.0, 2.0]",
+ "pos": "[5.0, 19.0754, 20.1865, 11.9693, 0, 0]",
+ "energy": 9.5,
+ "time": "2019-02-15T11:11:34.772000"
+ },
+ "3": {
+ "tag": "None",
+ "hkl": "[2.0, 2.0, 2.0]",
+ "pos": "[5.0, 27.1234, 21.2368, 60.0354, 0, 0]",
+ "energy": 9.5,
+ "time": "2019-02-15T11:11:34.864000"
+ }
+ },
+ "orientlist": {},
+ "tau": 0,
+ "sigma": 0,
+ "reference": {
+ "n_hkl_configured": null,
+ "n_phi_configured": "[0.0, 0.0, 1.0]"
+ },
+ "u": null,
+ "ub": null,
+ "or0": null,
+ "or1": null
+}
\ No newline at end of file
diff --git a/config/diffcalc/x.json b/config/diffcalc/x.json
new file mode 100644
index 0000000..8c4c9bf
--- /dev/null
+++ b/config/diffcalc/x.json
@@ -0,0 +1,38 @@
+{
+ "name": "x",
+ "crystal": "['cubic', 'Triclinic', 7.723, 7.707, 7.723, 90.0, 89.265, 90.0]",
+ "reflist": {
+ "1": {
+ "tag": "None",
+ "hkl": "[0.0, 1.0, 2.0]",
+ "pos": "[5.0, 9.379, 19.7895, 102.6162, 0, 0]",
+ "energy": 9.5,
+ "time": "2019-02-15T10:46:33.012000"
+ },
+ "2": {
+ "tag": "None",
+ "hkl": "[2.0, 0.0, 2.0]",
+ "pos": "[5.0, 19.0754, 20.1865, 11.9693, 0, 0]",
+ "energy": 9.5,
+ "time": "2019-02-15T10:46:33.094000"
+ },
+ "3": {
+ "tag": "None",
+ "hkl": "[2.0, 2.0, 2.0]",
+ "pos": "[5.0, 27.1234, 21.2368, 60.0354, 0, 0]",
+ "energy": 9.5,
+ "time": "2019-02-15T10:46:33.292000"
+ }
+ },
+ "orientlist": {},
+ "tau": 0,
+ "sigma": 0,
+ "reference": {
+ "n_hkl_configured": null,
+ "n_phi_configured": "[0.0, 0.0, 1.0]"
+ },
+ "u": null,
+ "ub": null,
+ "or0": null,
+ "or1": null
+}
\ No newline at end of file
diff --git a/config/jcae.properties b/config/jcae.properties
index 02ecf33..bcf0224 100755
--- a/config/jcae.properties
+++ b/config/jcae.properties
@@ -1,5 +1,5 @@
-#Fri Nov 16 23:08:57 CET 2018
-ch.psi.jcae.ContextFactory.addressList=127.0.0.1\:54321
+#Mon Feb 25 08:48:20 CET 2019
+ch.psi.jcae.ContextFactory.addressList=127.0.0.1\:54321 129.129.144.112
ch.psi.jcae.ContextFactory.maxArrayBytes=10000000
ch.psi.jcae.ChannelFactory.timeout=200
ch.psi.jcae.ChannelFactory.retries=1
diff --git a/config/plugins.properties b/config/plugins.properties
index c0f1b45..60c7d08 100755
--- a/config/plugins.properties
+++ b/config/plugins.properties
@@ -1,4 +1,4 @@
-PID.java=enabled
+MiniPID.java=enabled
SpinnerLayoutTest.java=disabled
MXSC-1.10.0.jar=disabled
ScreenPanel3.java=disabled
diff --git a/config/settings.properties b/config/settings.properties
new file mode 100644
index 0000000..2d0ef1d
--- /dev/null
+++ b/config/settings.properties
@@ -0,0 +1,3 @@
+#Tue Feb 26 18:02:18 CET 2019
+geometry=fourcv
+test=1
diff --git a/config/setup.properties b/config/setup.properties
index a54ff8f..5e0cee0 100755
--- a/config/setup.properties
+++ b/config/setup.properties
@@ -1,21 +1,21 @@
-#Wed Jan 10 09:07:30 CET 2018
-configFile={config}/config.properties
-configFileDevices={config}/devices.properties
-configFileImageSources={config}/imaging.properties
-configFilePlugins={config}/plugins.properties
-configFileTasks={config}/tasks.properties
-configFileUpdateStrategy={config}/update.properties
-configPath={home}/config
-contextPath={outp}/context
-dataPath={outp}/data
-devicesPath={home}/devices
-extensionsPath={home}/extensions
-imagesPath={outp}/images
-libraryPath={script}; {script}/Lib; src/main/assembly/script/tutorial
-#libraryPath={extensions}/JyNI.jar;{extensions};/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload;/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7;/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages;{script}; {script}/Lib; src/main/assembly/script/tutorial
-logPath={outp}/log
-pluginsPath={home}/plugins
-scriptPath={home}/script
-scriptType=py
-sessionsPath={outp}/sessions
-wwwPath={home}/www
+#Mon Dec 17 16:37:23 CET 2018
+configFile={config}/config.properties
+configFileDevices={config}/devices.properties
+configFileImageSources={config}/imaging.properties
+configFilePlugins={config}/plugins.properties
+configFileSettings={config}/settings.properties
+configFileTasks={config}/tasks.properties
+configFileUpdateStrategy={config}/update.properties
+configPath={home}/config
+contextPath={outp}/context
+dataPath={outp}/data
+devicesPath={home}/devices
+extensionsPath={home}/extensions
+imagesPath={outp}/images
+libraryPath={script}; {script}/Lib; src/main/assembly/script/tutorial
+logPath={outp}/log
+pluginsPath={home}/plugins
+scriptPath={home}/script
+scriptType=py
+sessionsPath={outp}/sessions
+wwwPath={home}/www
diff --git a/devices/CamServer.properties b/devices/CamServer.properties
index fa47746..a1484c5 100755
--- a/devices/CamServer.properties
+++ b/devices/CamServer.properties
@@ -1,24 +1,25 @@
-#Wed Oct 25 11:29:41 CEST 2017
-colormap=Grayscale
-colormapAutomatic=false
-colormapMax=NaN
-colormapMin=NaN
-flipHorizontally=false
-flipVertically=false
-grayscale=false
-invert=false
-rescaleFactor=1.0
-rescaleOffset=0.0
-roiHeight=-1
-roiWidth=-1
-roiX=0
-roiY=0
-rotation=0.0
-rotationCrop=false
-scale=1.0
-spatialCalOffsetX=NaN
-spatialCalOffsetY=NaN
-spatialCalScaleX=NaN
-spatialCalScaleY=NaN
-spatialCalUnits=mm
-transpose=false
+#Mon Mar 04 17:19:18 CET 2019
+colormap=Grayscale
+colormapAutomatic=false
+colormapLogarithmic=false
+colormapMax=NaN
+colormapMin=NaN
+flipHorizontally=false
+flipVertically=false
+grayscale=false
+invert=false
+rescaleFactor=1.0
+rescaleOffset=0.0
+roiHeight=-1
+roiWidth=-1
+roiX=0
+roiY=0
+rotation=0.0
+rotationCrop=false
+scale=1.0
+spatialCalOffsetX=NaN
+spatialCalOffsetY=NaN
+spatialCalScaleX=NaN
+spatialCalScaleY=NaN
+spatialCalUnits=mm
+transpose=false
diff --git a/devices/Camera Server.properties b/devices/Camera Server.properties
index 26ab0a1..2fa5c84 100755
--- a/devices/Camera Server.properties
+++ b/devices/Camera Server.properties
@@ -1,25 +1,26 @@
-#Mon Oct 23 11:55:12 CEST 2017
-colormap=Grayscale
-colormapAutomatic=false
-colormapMax=NaN
-colormapMin=NaN
-flipHorizontally=false
-flipVertically=false
-grayscale=false
-invert=false
-rescaleFactor=1.0
-rescaleOffset=0.0
-roiHeight=-1
-roiWidth=-1
-roiX=0
-roiY=0
-rotation=0.0
-rotationCrop=false
-scale=1.0
-serverURL=localhost\:8888
-spatialCalOffsetX=NaN
-spatialCalOffsetY=NaN
-spatialCalScaleX=NaN
-spatialCalScaleY=NaN
-spatialCalUnits=mm
-transpose=false
+#Mon Mar 04 17:19:22 CET 2019
+colormap=Grayscale
+colormapAutomatic=false
+colormapLogarithmic=false
+colormapMax=NaN
+colormapMin=NaN
+flipHorizontally=false
+flipVertically=false
+grayscale=false
+invert=false
+rescaleFactor=1.0
+rescaleOffset=0.0
+roiHeight=-1
+roiWidth=-1
+roiX=0
+roiY=0
+rotation=0.0
+rotationCrop=false
+scale=1.0
+serverURL=localhost\:8888
+spatialCalOffsetX=NaN
+spatialCalOffsetY=NaN
+spatialCalScaleX=NaN
+spatialCalScaleY=NaN
+spatialCalUnits=mm
+transpose=false
diff --git a/devices/CurrentCamera.properties b/devices/CurrentCamera.properties
index a103678..fb9fd03 100755
--- a/devices/CurrentCamera.properties
+++ b/devices/CurrentCamera.properties
@@ -1,9 +1,9 @@
-#Thu Aug 09 15:37:16 CEST 2018
-colormap=Flame
+#Wed Mar 20 10:49:26 CET 2019
+colormap=Temperature
colormapAutomatic=true
colormapLogarithmic=false
-colormapMax=25300.0
-colormapMin=0.0
+colormapMax=10000.0
+colormapMin=100.0
custom=12345
flipHorizontally=false
flipVertically=false
@@ -23,9 +23,9 @@ rotation=0.0
rotationCrop=false
scale=1.0
serverURL=http\://gfa-lc6-64\:8889
-spatialCalOffsetX=-131.47810112809645
-spatialCalOffsetY=-114.68674851532894
-spatialCalScaleX=-26.95417819057938
-spatialCalScaleY=-35.84229324524661
+spatialCalOffsetX=-637.4980411378614
+spatialCalOffsetY=-483.5036425564949
+spatialCalScaleX=-35.21126887460907
+spatialCalScaleY=-48.38709552593848
spatialCalUnits=mm
transpose=false
diff --git a/devices/Image Averager.properties b/devices/Image Averager.properties
index 2d7a85f..9dccf8a 100644
--- a/devices/Image Averager.properties
+++ b/devices/Image Averager.properties
@@ -1,6 +1,7 @@
-#Tue May 15 17:44:18 CEST 2018
+#Tue Mar 05 11:53:08 CET 2019
colormap=Flame
colormapAutomatic=true
+colormapLogarithmic=false
colormapMax=50660.0
colormapMin=0.0
flipHorizontally=false
@@ -16,9 +17,9 @@ roiY=0
rotation=0.0
rotationCrop=false
scale=1.0
-spatialCalOffsetX=0.0
-spatialCalOffsetY=0.0
-spatialCalScaleX=1.0
-spatialCalScaleY=1.0
+spatialCalOffsetX=-637.4980411378614
+spatialCalOffsetY=-483.5036425564949
+spatialCalScaleX=-35.21126887460907
+spatialCalScaleY=-48.38709552593848
spatialCalUnits=mm
transpose=false
diff --git a/devices/WireScanner motor.properties b/devices/WireScanner motor.properties
new file mode 100644
index 0000000..43bb8cb
--- /dev/null
+++ b/devices/WireScanner motor.properties
@@ -0,0 +1,17 @@
+#Mon Feb 25 15:03:11 CET 2019
+defaultSpeed=282.842712474619
+estbilizationDelay=0
+hasEnable=false
+homingType=None
+maxSpeed=10000.0
+maxValue=51500.0
+minSpeed=NaN
+minValue=-56789.2
+offset=0.0
+precision=1
+resolution=0.5
+rotation=false
+scale=1.0
+sign_bit=0
+startRetries=1
+unit=mm
diff --git a/devices/as1.properties b/devices/as1.properties
index 9ad1f88..b0d9589 100644
--- a/devices/as1.properties
+++ b/devices/as1.properties
@@ -1,4 +1,4 @@
-#Tue Oct 30 09:23:19 CET 2018
+#Thu Feb 21 09:17:18 CET 2019
colormap=Grayscale
colormapAutomatic=false
colormapLogarithmic=false
diff --git a/devices/cam_server.properties b/devices/cam_server.properties
index 61e604e..9c167cb 100755
--- a/devices/cam_server.properties
+++ b/devices/cam_server.properties
@@ -1,4 +1,4 @@
-#Thu Aug 09 15:41:34 CEST 2018
+#Mon Jan 21 11:07:44 CET 2019
colormap=Temperature
colormapAutomatic=true
colormapLogarithmic=false
@@ -18,9 +18,9 @@ roiY=0
rotation=0.0
rotationCrop=false
scale=1.0
-spatialCalOffsetX=-50.048875855327466
-spatialCalOffsetY=-50.048875855327466
-spatialCalScaleX=-1.0
-spatialCalScaleY=-1.0
+spatialCalOffsetX=0.0
+spatialCalOffsetY=0.0
+spatialCalScaleX=1.0
+spatialCalScaleY=1.0
spatialCalUnits=mm
transpose=false
diff --git a/devices/cam_server_local.properties b/devices/cam_server_local.properties
index 292f8fb..fdd0b82 100644
--- a/devices/cam_server_local.properties
+++ b/devices/cam_server_local.properties
@@ -1,6 +1,6 @@
-#Thu Aug 09 15:38:40 CEST 2018
-colormap=Grayscale
-colormapAutomatic=false
+#Fri Dec 14 13:50:04 CET 2018
+colormap=Temperature
+colormapAutomatic=true
colormapLogarithmic=false
colormapMax=NaN
colormapMin=NaN
@@ -17,9 +17,9 @@ roiY=0
rotation=0.0
rotationCrop=false
scale=1.0
-spatialCalOffsetX=NaN
-spatialCalOffsetY=NaN
-spatialCalScaleX=NaN
-spatialCalScaleY=NaN
+spatialCalOffsetX=-634.4956973840979
+spatialCalOffsetY=-509.5307480984911
+spatialCalScaleX=-26.954178679632527
+spatialCalScaleY=-35.8422927936001
spatialCalUnits=mm
transpose=false
diff --git a/devices/delta.properties b/devices/delta.properties
index 749f56b..8680446 100644
--- a/devices/delta.properties
+++ b/devices/delta.properties
@@ -1,10 +1,10 @@
-#Fri Aug 24 11:04:46 CEST 2018
+#Mon Feb 11 17:24:00 CET 2019
defaultSpeed=50.0
estbilizationDelay=0
maxSpeed=50.0
-maxValue=360.0
+maxValue=190.0
minSpeed=0.1
-minValue=-360.0
+minValue=-19.0
offset=0.0
precision=2
resolution=NaN
diff --git a/devices/deting.properties b/devices/deting.properties
index 2bccd75..e811207 100644
--- a/devices/deting.properties
+++ b/devices/deting.properties
@@ -1,9 +1,9 @@
-#Thu Sep 13 17:21:24 CEST 2018
-colormap=Grayscale
+#Thu Mar 14 16:06:34 CET 2019
+colormap=Flame
colormapAutomatic=false
colormapLogarithmic=false
-colormapMax=NaN
-colormapMin=NaN
+colormapMax=25000.0
+colormapMin=0.0
flipHorizontally=false
flipVertically=false
grayscale=false
diff --git a/devices/eta.properties b/devices/eta.properties
index ac93a71..5fe774f 100644
--- a/devices/eta.properties
+++ b/devices/eta.properties
@@ -1,10 +1,10 @@
-#Fri Aug 24 11:04:56 CEST 2018
+#Mon Feb 11 17:20:20 CET 2019
defaultSpeed=50.0
estbilizationDelay=0
maxSpeed=50.0
-maxValue=360.0
+maxValue=189.0
minSpeed=0.1
-minValue=-360.0
+minValue=-190.0
offset=0.0
precision=2
resolution=NaN
diff --git a/devices/gamma.properties b/devices/gamma.properties
index 4101765..27b77b2 100644
--- a/devices/gamma.properties
+++ b/devices/gamma.properties
@@ -1,10 +1,10 @@
-#Fri Aug 24 11:49:29 CEST 2018
+#Mon Feb 11 17:19:49 CET 2019
defaultSpeed=50.0
estbilizationDelay=0
maxSpeed=50.0
-maxValue=180.0
+maxValue=164.0
minSpeed=0.1
-minValue=-180.0
+minValue=-61.0
offset=0.0
precision=2
resolution=NaN
diff --git a/devices/mu.properties b/devices/mu.properties
index a899eb5..0967cfc 100644
--- a/devices/mu.properties
+++ b/devices/mu.properties
@@ -1,10 +1,10 @@
-#Fri Aug 24 11:05:25 CEST 2018
+#Mon Feb 11 17:18:43 CET 2019
defaultSpeed=50.0
estbilizationDelay=0
maxSpeed=50.0
-maxValue=360.0
+maxValue=116.0
minSpeed=0.1
-minValue=-360.0
+minValue=-13.0
offset=0.0
precision=2
resolution=NaN
diff --git a/devices/src1.properties b/devices/src1.properties
index 60fcd6f..289ef71 100755
--- a/devices/src1.properties
+++ b/devices/src1.properties
@@ -1,13 +1,13 @@
-#Mon Aug 06 10:26:59 CEST 2018
+#Fri Mar 15 07:43:23 CET 2019
calOffsetX=NaN
calOffsetY=NaN
calScaleX=NaN
calScaleY=NaN
colormap=Temperature
-colormapAutomatic=true
+colormapAutomatic=false
colormapLogarithmic=false
-colormapMax=255.0
-colormapMin=0.0
+colormapMax=NaN
+colormapMin=NaN
dataMonitoring=false
dataPolling=100
flipHorizontally=true
diff --git a/plugins/PID.java b/plugins/MiniPID.java
similarity index 99%
rename from plugins/PID.java
rename to plugins/MiniPID.java
index 62f5bea..c520e3b 100644
--- a/plugins/PID.java
+++ b/plugins/MiniPID.java
@@ -1,3 +1,4 @@
+//package com.stormbots;
/**
* Small, easy to use PID implementation with advanced controller capability.
* Minimal usage:
@@ -8,7 +9,7 @@
*
* @see http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-direction/improving-the-beginners-pid-introduction
*/
-public class PID{
+public class MiniPID{
//**********************************
// Class private variables
//**********************************
@@ -50,7 +51,7 @@ public class PID{
* @param i Integral gain. Becomes large if setpoint cannot reach target quickly.
* @param d Derivative gain. Responds quickly to large changes in error. Small values prevents P and I terms from causing overshoot.
*/
- public PID(double p, double i, double d){
+ public MiniPID(double p, double i, double d){
P=p; I=i; D=d;
checkSigns();
}
@@ -63,7 +64,7 @@ public class PID{
* @param d Derivative gain. Responds quickly to large changes in error. Small values prevents P and I terms from causing overshoot.
* @param f Feed-forward gain. Open loop "best guess" for the output should be. Only useful if setpoint represents a rate.
*/
- public PID(double p, double i, double d, double f){
+ public MiniPID(double p, double i, double d, double f){
P=p; I=i; D=d; F=f;
checkSigns();
}
diff --git a/plugins/ScreenPanel3.form b/plugins/ScreenPanel3.form
index 8f0edfa..4584b9c 100644
--- a/plugins/ScreenPanel3.form
+++ b/plugins/ScreenPanel3.form
@@ -1053,6 +1053,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/ScreenPanel3.java b/plugins/ScreenPanel3.java
index 6bc280b..719a085 100644
--- a/plugins/ScreenPanel3.java
+++ b/plugins/ScreenPanel3.java
@@ -523,6 +523,7 @@ public class ScreenPanel3 extends Panel {
}
});
renderer.getPopupMenu().setVisible(false);
+ buttonScale.setSelected(renderer.getShowColormapScale());
clearMarker();
showFit = buttonFit.isSelected();
@@ -1000,7 +1001,12 @@ public class ScreenPanel3 extends Panel {
imageBuffer.add(currentFrame);
if (imageBuffer.size() > imageBufferLenght) {
imageBuffer.remove(0);
+ setBufferFull(true);
+ } else {
+ setBufferFull(false);
}
+ } else {
+ setBufferFull(true);
}
//Update data
if (!renderer.isPaused()) {
@@ -1108,16 +1114,24 @@ public class ScreenPanel3 extends Panel {
@Override
public void onImage(Object o, BufferedImage bi, Data data) {
- if (continuous) {
- buffer.add(data);
- if (buffer.size() >= numImages) {
- for (Data d : buffer) {
- process(d);
+ try{
+ if (continuous) {
+ buffer.add(data);
+ if (buffer.size() >= numImages) {
+ for (Data d : buffer) {
+ process(d);
+ }
}
+ } else {
+ buffer.add(null); //Just to count
+ process(data);
}
- } else {
- buffer.add(null); //Just to count
- process(data);
+ } catch (Exception ex){
+ buffer.clear();
+ integration = null;
+ ImageIntegrator.this.pushData(null);
+ ex.printStackTrace();
+ return;
}
if (buffer.size() >= numImages) {
if (continuous) {
@@ -1148,6 +1162,17 @@ public class ScreenPanel3 extends Panel {
}
}
+
+ boolean bufferFull = true;
+
+ void setBufferFull(boolean value){
+ if (value != bufferFull){
+ SwingUtilities.invokeLater(()->{
+ buttonPause.setBackground(value ? buttonSave.getBackground() : buttonSave.getBackground().brighter());
+ });
+ bufferFull = value;
+ }
+ }
volatile Dimension imageSize;
@@ -1163,6 +1188,7 @@ public class ScreenPanel3 extends Panel {
renderer.refresh();
}
+
void checkMarker(Point p) throws IOException {
if (camera != null) {
if (buttonMarker.isSelected()) {
@@ -1477,7 +1503,7 @@ public class ScreenPanel3 extends Panel {
updateColormap();
updateButtons();
checkHistogram.setSelected((histogramDialog != null) && (histogramDialog.isShowing()));
-
+ buttonScale.setSelected(renderer.getShowColormapScale());
try{
Frame frame = getCurrentFrame();
if (frame!=lastFrame){
@@ -2652,6 +2678,7 @@ public class ScreenPanel3 extends Panel {
buttonProfile = new javax.swing.JToggleButton();
buttonFit = new javax.swing.JToggleButton();
buttonReticle = new javax.swing.JToggleButton();
+ buttonScale = new javax.swing.JToggleButton();
buttonTitle = new javax.swing.JToggleButton();
pauseSelection = new ch.psi.pshell.swing.ValueSelection();
panelCameraSelection = new javax.swing.JPanel();
@@ -3335,6 +3362,19 @@ public class ScreenPanel3 extends Panel {
});
toolBar.add(buttonReticle);
+ buttonScale.setIcon(getIcon("Scale"));
+ buttonScale.setSelected(true);
+ buttonScale.setText(" ");
+ buttonScale.setToolTipText("Show Colormap Scale");
+ buttonScale.setFocusable(false);
+ buttonScale.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonScale.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonScaleActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonScale);
+
buttonTitle.setIcon(getIcon("Title"));
buttonTitle.setSelected(true);
buttonTitle.setText(" ");
@@ -3963,6 +4003,14 @@ public class ScreenPanel3 extends Panel {
}
}//GEN-LAST:event_buttonTitleActionPerformed
+ private void buttonScaleActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonScaleActionPerformed
+ try {
+ renderer.setShowColormapScale(buttonScale.isSelected());
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonScaleActionPerformed
+
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton btFixColormapRange;
private javax.swing.JRadioButton buttonAutomatic;
@@ -3980,6 +4028,7 @@ public class ScreenPanel3 extends Panel {
private javax.swing.JToggleButton buttonProfile;
private javax.swing.JToggleButton buttonReticle;
private javax.swing.JToggleButton buttonSave;
+ private javax.swing.JToggleButton buttonScale;
private javax.swing.JRadioButton buttonServer;
private javax.swing.JToggleButton buttonSidePanel;
private javax.swing.JButton buttonStreamData;
diff --git a/plugins/ScreenPanel3New.java b/plugins/ScreenPanel3New.java
new file mode 100644
index 0000000..6678365
--- /dev/null
+++ b/plugins/ScreenPanel3New.java
@@ -0,0 +1,4067 @@
+/*
+ * Copyright (c) 2014 Paul Scherrer Institute. All rights reserved.
+ */
+
+import ch.psi.pshell.bs.CameraServer;
+import ch.psi.pshell.core.Context;
+import java.io.IOException;
+import java.nio.file.Paths;
+import javax.swing.DefaultComboBoxModel;
+import ch.psi.pshell.ui.Panel;
+import ch.psi.pshell.imaging.ImageListener;
+import ch.psi.utils.State;
+import ch.psi.utils.Chrono;
+import ch.psi.utils.swing.SwingUtils;
+import ch.psi.utils.swing.TextEditor;
+import ch.psi.pshell.bs.PipelineServer;
+import ch.psi.pshell.bs.StreamValue;
+import ch.psi.pshell.core.JsonSerializer;
+import ch.psi.pshell.data.DataManager;
+import ch.psi.pshell.device.Device;
+import ch.psi.pshell.device.Readable.ReadableArray;
+import ch.psi.pshell.device.Readable.ReadableNumber;
+import ch.psi.pshell.device.ReadableRegister.ReadableRegisterArray;
+import ch.psi.pshell.device.ReadableRegister.ReadableRegisterNumber;
+import ch.psi.pshell.epics.ChannelInteger;
+import ch.psi.pshell.epics.DiscretePositioner;
+import ch.psi.pshell.epics.Epics;
+import ch.psi.pshell.imaging.Colormap;
+import ch.psi.pshell.imaging.ColormapSource;
+import ch.psi.pshell.imaging.ColormapSource.ColormapSourceConfig;
+import ch.psi.pshell.ui.App;
+import ch.psi.pshell.imaging.Data;
+import ch.psi.pshell.imaging.DimensionDouble;
+import ch.psi.pshell.imaging.Histogram;
+import ch.psi.pshell.imaging.ImageBuffer;
+import ch.psi.pshell.imaging.Overlay;
+import ch.psi.pshell.imaging.Overlays;
+import ch.psi.pshell.imaging.Overlays.Text;
+import ch.psi.pshell.imaging.Pen;
+import ch.psi.pshell.imaging.PointDouble;
+import ch.psi.pshell.imaging.Renderer;
+import ch.psi.pshell.imaging.RendererListener;
+import ch.psi.pshell.imaging.RendererMode;
+import ch.psi.pshell.imaging.Source;
+import ch.psi.pshell.plot.PlotBase;
+import ch.psi.pshell.scripting.InterpreterResult;
+import ch.psi.pshell.scripting.ScriptManager;
+import ch.psi.pshell.swing.DeviceValueChart;
+import ch.psi.pshell.swing.ValueSelection;
+import ch.psi.pshell.swing.ValueSelection.ValueSelectionListener;
+import ch.psi.pshell.ui.Console;
+import ch.psi.utils.Arr;
+import ch.psi.utils.ArrayProperties;
+import ch.psi.utils.Convert;
+import ch.psi.utils.Str;
+import ch.psi.utils.swing.Editor.EditorDialog;
+import ch.psi.utils.swing.MainFrame;
+import ch.psi.utils.swing.StandardDialog;
+import ch.psi.utils.swing.StandardDialog.StandardDialogListener;
+import ch.psi.utils.swing.SwingUtils.OptionResult;
+import ch.psi.utils.swing.SwingUtils.OptionType;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.Transferable;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileInputStream;
+import java.lang.reflect.Array;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSpinner;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+import javax.swing.table.DefaultTableModel;
+import org.apache.commons.math3.analysis.function.Gaussian;
+import org.apache.commons.math3.fitting.GaussianCurveFitter;
+import org.apache.commons.math3.fitting.PolynomialCurveFitter;
+import org.apache.commons.math3.fitting.WeightedObservedPoint;
+import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
+
+/**
+ *
+ */
+public class ScreenPanel3New extends Panel {
+
+ public static final String LASER_TYPE = "Laser";
+ public static final String ELECTRONS_TYPE = "Electrons";
+ public static final String PHOTONICS_TYPE = "Photonics";
+
+ final String CAMERA_DEVICE_NAME = "CurrentCamera";
+ boolean useServerStats = true;
+ String userOverlaysConfigFile;
+ ColormapSource camera;
+ PipelineServer server;
+ String cameraName;
+ int polling = 1000;
+ Overlay marker = null;
+ JDialog histogramDialog;
+ DiscretePositioner screen;
+ DiscretePositioner filter;
+ boolean showFit;
+ boolean showProfile;
+ Overlay[] userOv;
+ Overlay[] fitOv;
+ Overlay[] profileOv;
+ Overlay errorOverlay;
+ boolean requestCameraListUpdate;
+ boolean goodRegion;
+ boolean slicing;
+ String serverUrl;
+ String camServerUrl;
+ String instanceName;
+ Overlay titleOv = null;
+ int integration = 0;
+
+ String pipelineSuffix = "_sp";
+
+ Double getServerDouble(String name) {
+ return (Double) Convert.toDouble(server.getValue(name));
+ }
+
+ double[] getServerDoubleArray(String name) {
+ return (double[]) Convert.toDouble(server.getValue(name));
+ }
+
+ Double getServerDouble(String name, StreamValue cache) {
+ return (Double) Convert.toDouble(cache.__getitem__(name));
+ }
+
+ double[] getServerDoubleArray(String name, StreamValue cache) {
+ return (double[]) Convert.toDouble(cache.__getitem__(name));
+ }
+
+ class ImageData {
+
+ ImageData() {
+ if (server != null) {
+ cache = server.getStream().take();
+ String prefix = goodRegion ? "gr_" : "";
+ x_fit_mean = getServerDouble(prefix + "x_fit_mean", cache);
+ y_fit_mean = getServerDouble(prefix + "y_fit_mean", cache);
+ x_fit_standard_deviation = getServerDouble(prefix + "x_fit_standard_deviation", cache);
+ y_fit_standard_deviation = getServerDouble(prefix + "y_fit_standard_deviation", cache);
+ x_fit_gauss_function = getServerDoubleArray(prefix + "x_fit_gauss_function", cache);
+ y_fit_gauss_function = getServerDoubleArray(prefix + "y_fit_gauss_function", cache);
+ x_profile = getServerDoubleArray("x_profile", cache);
+ y_profile = getServerDoubleArray("y_profile", cache);
+ x_center_of_mass = getServerDouble("x_center_of_mass", cache);
+ y_center_of_mass = getServerDouble("y_center_of_mass", cache);
+ x_rms = getServerDouble("x_rms", cache);
+ y_rms = getServerDouble("y_rms", cache);
+ if (goodRegion) {
+ double[] gX2 = new double[x_profile.length];
+ Arrays.fill(gX2, Double.NaN);
+ try {
+ double x = getServerDoubleArray("gr_x_axis", cache)[0];
+ gr_size_x = x_fit_gauss_function.length;
+ gr_pos_x = (int) ((renderer.getCalibration() != null) ? renderer.getCalibration().convertToImageX(x) : x);
+ System.arraycopy(x_fit_gauss_function, 0, gX2, gr_pos_x, gr_size_x);
+ } catch (Exception ex) {
+ }
+ x_fit_gauss_function = gX2;
+ double[] gY2 = new double[y_profile.length];
+ Arrays.fill(gY2, Double.NaN);
+ try {
+ double y = getServerDoubleArray("gr_y_axis", cache)[0];
+ gr_size_y = y_fit_gauss_function.length;
+ gr_pos_y = (int) ((renderer.getCalibration() != null) ? renderer.getCalibration().convertToImageY(y) : y);
+ System.arraycopy(y_fit_gauss_function, 0, gY2, gr_pos_y, y_fit_gauss_function.length);
+ } catch (Exception ex) {
+ }
+ y_fit_gauss_function = gY2;
+ if (slicing) {
+ try {
+ int slices = getServerDouble("slice_amount").intValue();
+ sliceCenters = new PointDouble[slices];
+ for (int i = 0; i < slices; i++) {
+ double x = getServerDouble("slice_" + i + "_center_x");
+ double y = getServerDouble("slice_" + i + "_center_y");
+ sliceCenters[i] = new PointDouble(x, y);
+ }
+ } catch (Exception ex) {
+ }
+ }
+ }
+ }
+ }
+ public Double x_fit_mean;
+ public Double y_fit_mean;
+ public Double x_center_of_mass;
+ public Double x_rms;
+ public Double x_fit_standard_deviation;
+ public Double y_fit_standard_deviation;
+ public Double y_center_of_mass;
+ public Double y_rms;
+ public double[] x_profile;
+ public double[] x_fit_gauss_function;
+ public double[] y_profile;
+ public double[] y_fit_gauss_function;
+ public int gr_size_x;
+ public int gr_pos_x;
+ public int gr_size_y;
+ public int gr_pos_y;
+ public PointDouble[] sliceCenters;
+ public StreamValue cache;
+ }
+
+ class Frame extends ImageData {
+
+ Frame(Data data) {
+ this.data = data;
+ }
+ Data data;
+ }
+
+ final ArrayList imageBuffer = new ArrayList();
+ Frame currentFrame;
+ int imageBufferLenght = 1;
+ Text imagePauseOverlay;
+ final Console console;
+
+ public ScreenPanel3New() {
+ try {
+ initComponents();
+ spinnerThreshold.setVisible(false);
+ btFixColormapRange.setVisible(false);
+ setGoodRegionOptionsVisible(false);
+ setSlicingOptionsVisible(false);
+ JComponent editor = spinnerSlOrientation.getEditor();
+ if (editor instanceof JSpinner.DefaultEditor) {
+ ((JSpinner.DefaultEditor) editor).getTextField().setHorizontalAlignment(JTextField.RIGHT);
+ }
+ renderer.setPersistenceFile(Paths.get(getContext().getSetup().getContextPath(), "Renderer_Cameras.bin"));
+ //setPersistedComponents(new Component[]{buttonServer, buttonDirect});
+ comboCameras.setEnabled(false);
+ comboType.setEnabled(false);
+
+ SwingUtils.setEnumCombo(comboColormap, Colormap.class);
+ if (App.hasArgument("poll")) {
+ try {
+ polling = Integer.valueOf(App.getArgumentValue("poll"));
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ if (App.hasArgument("zoom")) {
+ try {
+ renderer.setDefaultZoom(Double.valueOf(App.getArgumentValue("zoom")));
+ renderer.resetZoom();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ if (App.hasArgument("buf")) {
+ try {
+ imageBufferLenght = Integer.valueOf(App.getArgumentValue("buf"));
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ if (App.hasArgument("usr_ov")) {
+ try {
+ userOverlaysConfigFile = App.getArgumentValue("usr_ov");
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ if (App.hasArgument("srv_url")) {
+ serverUrl = App.getArgumentValue("srv_url");
+ }
+
+ if (App.hasArgument("cam_srv_url")) {
+ camServerUrl = App.getArgumentValue("cam_srv_url");
+ }
+
+ if (App.hasArgument("calc")) {
+ useServerStats = false;
+ }
+ if (App.hasArgument("suffix")) {
+ pipelineSuffix = App.getArgumentValue("suffix");
+ }
+ if (App.hasArgument("integration")) {
+ try {
+ setIntegration(Integer.valueOf(App.getArgumentValue("integration")));
+ if (integration != 0) {
+ buttonFit.setSelected(false);
+ buttonProfile.setSelected(false);
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ renderer.setProfileNormalized(true);
+ renderer.setShowProfileLimits(false);
+
+ JMenuItem menuCalibrate = new JMenuItem("Calibrate...");
+ menuCalibrate.addActionListener((ActionEvent e) -> {
+ try {
+ calibrate();
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ });
+
+ JMenuItem menuRendererConfig = new JMenuItem("Renderer Parameters");
+ menuRendererConfig.addActionListener((ActionEvent e) -> {
+ try {
+ if (camera != null) {
+ this.showDeviceConfigDialog(camera, false);
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ });
+
+ JMenuItem menuCameraConfig = new JMenuItem("Camera Configurarion");
+ menuCameraConfig.addActionListener((ActionEvent e) -> {
+ try {
+ if (camera != null) {
+ String cameraConfigJson = null;
+ if (usingServer) {
+ String cameraServerUrl = (camServerUrl == null) ? server.getUrl().substring(0, server.getUrl().length() - 1) + "8" : camServerUrl;
+ try (CameraServer srv = new CameraServer("CamServer", cameraServerUrl)) {
+ srv.initialize();
+ //TODO: replace into encodeMultiline
+ cameraConfigJson = JsonSerializer.encode(srv.getConfig(cameraName), true);
+ }
+
+ } else {
+ String configFolder = (String) getContext().getClassByName("SfCamera").getMethod("getConfigFolder", new Class[]{}).invoke(null);
+ Path configFile = Paths.get(configFolder, cameraName + ".json");
+ cameraConfigJson = configFile.toFile().exists() ? new String(Files.readAllBytes(configFile)) : null;
+ }
+ TextEditor configEditor = new TextEditor();
+ configEditor.setText(cameraConfigJson);
+ configEditor.setReadOnly(true);
+ configEditor.setTitle(cameraName);
+ EditorDialog dlg = configEditor.getDialog(getTopLevel(), false);
+ dlg.setSize(480, 640);
+ dlg.setVisible(true);
+ SwingUtils.centerComponent(getTopLevel(), dlg);
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ });
+
+ JMenuItem menuSetImageBufferSize = new JMenuItem("Set Stack Size...");
+ menuSetImageBufferSize.addActionListener((ActionEvent e) -> {
+ try {
+ String ret = SwingUtils.getString(getTopLevel(), "Enter size of image buffer: ", String.valueOf(imageBufferLenght));
+ if (ret != null) {
+ this.setImageBufferSize(Integer.valueOf(ret));
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ });
+
+ JMenuItem menuSaveStack = new JMenuItem("Save Stack");
+ menuSaveStack.addActionListener((ActionEvent e) -> {
+ try {
+ saveStack();
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ });
+
+ JMenuItem menuSetROI = new JMenuItem("Set ROI...");
+ menuSetROI.addActionListener((ActionEvent e) -> {
+ renderer.abortSelection();
+ if (server != null) {
+ final Overlays.Rect selection = new Overlays.Rect(renderer.getPenMovingOverlay());
+ renderer.addListener(new RendererListener() {
+ @Override
+ public void onSelectionFinished(Renderer renderer, Overlay overlay) {
+ try {
+ renderer.setShowReticle(false);
+ Rectangle roi = overlay.isFixed() ? renderer.toImageCoord(overlay.getBounds()) : overlay.getBounds();
+ if (server.isRoiEnabled()) {
+ int[] cur = server.getRoi();
+ server.setRoi(new int[]{roi.x + cur[0], roi.y + cur[1], roi.width, roi.height});
+ } else {
+ server.setRoi(new int[]{roi.x, roi.y, roi.width, roi.height});
+ }
+ } catch (Exception ex) {
+ } finally {
+ renderer.removeListener(this);
+ }
+ }
+
+ @Override
+ public void onSelectionAborted(Renderer renderer, Overlay overlay) {
+ renderer.removeListener(this);
+ }
+ });
+ selection.setFixed(true);
+ renderer.startSelection(selection);
+ }
+ });
+
+ JMenuItem menuResetROI = new JMenuItem("Reset ROI");
+ menuResetROI.addActionListener((ActionEvent e) -> {
+ renderer.abortSelection();
+ if (server != null) {
+ try {
+ renderer.setShowReticle(false);
+ server.resetRoi();
+ } catch (IOException ex) {
+ showException(ex);
+ }
+ }
+ });
+
+ JCheckBoxMenuItem menuFrameIntegration = new JCheckBoxMenuItem("Multi-Frame", (integration != 0));
+ menuFrameIntegration.addActionListener((ActionEvent e) -> {
+ if (integration == 0) {
+ JPanel panel = new JPanel();
+ GridBagLayout layout = new GridBagLayout();
+ layout.columnWidths = new int[]{150, 50}; //Minimum width
+ layout.rowHeights = new int[]{30, 30}; //Minimum height
+ panel.setLayout(layout);
+ JCheckBox checkContinuous = new JCheckBox("");
+ JTextField textFrames = new JTextField();
+ GridBagConstraints c = new GridBagConstraints();
+ c.gridx = 0;
+ c.gridy = 0;
+ panel.add(new JLabel("Number of frames:"), c);
+ c.gridy = 1;
+ panel.add(new JLabel("Continuous:"), c);
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridx = 1;
+ panel.add(checkContinuous, c);
+ c.gridy = 0;
+ panel.add(textFrames, c);
+ if (SwingUtils.showOption(getTopLevel(), "Multi-Frame Integration", panel, OptionType.OkCancel) == OptionResult.Yes) {
+ setIntegration(checkContinuous.isSelected() ? -(Integer.valueOf(textFrames.getText())) : (Integer.valueOf(textFrames.getText())));
+ }
+ } else {
+ if (SwingUtils.showOption(getTopLevel(), "Multi-Frame Integration",
+ "Do you want to disable " + ((integration < 0) ? "continuous " : "") + "multi-frame integration (" + Math.abs(integration) + ")?", OptionType.YesNo) == OptionResult.Yes) {
+ setIntegration(0);
+ }
+ }
+ });
+
+ for (Component cmp : SwingUtils.getComponentsByType(renderer.getPopupMenu(), JMenu.class)) {
+ JMenu menu = (JMenu) cmp;
+ if (menu.getText().equals("Integration")) {
+ menu.addSeparator();
+ menu.add(menuFrameIntegration);
+ }
+ }
+ renderer.getPopupMenu().addSeparator();
+ renderer.getPopupMenu().add(menuRendererConfig);
+ renderer.getPopupMenu().add(menuCameraConfig);
+ renderer.getPopupMenu().add(menuSetImageBufferSize);
+ renderer.getPopupMenu().add(menuSaveStack);
+ renderer.getPopupMenu().addSeparator();
+ renderer.getPopupMenu().add(menuCalibrate);
+ renderer.getPopupMenu().addSeparator();
+ renderer.getPopupMenu().add(menuSetROI);
+ renderer.getPopupMenu().add(menuResetROI);
+ renderer.getPopupMenu().addPopupMenuListener(new PopupMenuListener() {
+ @Override
+ public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
+ menuResetROI.setEnabled(server != null);
+ menuSetROI.setEnabled(server != null);
+ menuCalibrate.setVisible(server != null);
+ menuCalibrate.setEnabled((calibrationDialolg == null) || (!calibrationDialolg.isShowing()));
+ menuSaveStack.setEnabled(imageBufferLenght > 0);
+ menuSetImageBufferSize.setEnabled(!renderer.isPaused());
+ menuFrameIntegration.setSelected(integration != 0);
+ }
+
+ @Override
+ public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
+ }
+
+ @Override
+ public void popupMenuCanceled(PopupMenuEvent e) {
+ }
+ });
+ renderer.getPopupMenu().setVisible(false);
+ clearMarker();
+
+ showFit = buttonFit.isSelected();
+ showProfile = buttonProfile.isSelected();
+
+ pauseSelection.setVisible(false);
+ pauseSelection.setMinValue(1);
+ pauseSelection.addListener(new ValueSelectionListener() {
+ @Override
+ public void onValueChanged(ValueSelection origin, double value, boolean editing) {
+ if (editing && (value >= 1) && (value <= imageBuffer.size())) {
+ updatePause();
+ }
+ }
+ });
+ renderer.addListener(new RendererListener() {
+ @Override
+ public void onMoveFinished(Renderer renderer, Overlay overlay) {
+ if (overlay == marker) {
+ try {
+ onMarkerChanged();
+ } catch (IOException ex) {
+ Logger.getLogger(ScreenPanel3New.class.getName()).log(Level.WARNING, null, ex);
+ }
+ }
+ }
+ });
+ if (MainFrame.isDark()) {
+ textState.setDisabledTextColor(textState.getForeground());
+ }
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+
+ console = (!App.hasArgument("console")) ? null : new Console() {
+ /*
+ protected void onConsoleCommand(String name, String[] pars, String trimming) throws Exception {
+ switch (name) {
+ case "cam":
+ comboCameras.setSelectedItem(tokens[1]);
+ break;
+ }
+ }*/
+ @Override
+ protected void onConsoleCommand(String command) {
+ String[] tokens = command.split(" ");
+ if ((tokens.length > 1) && tokens[0].equals("cam")){
+ try{
+ if (!tokens[1].equals(comboCameras.getSelectedItem())){
+ setComboTypeSelection("All");
+ updateCameraList();
+ comboCameras.setSelectedItem(tokens[1]);
+ if (!tokens[1].equals(comboCameras.getSelectedItem())){
+ throw new Exception("Invalid camera name : " + tokens[1]);
+ }
+ System.out.println("Console set camera: " + tokens[1]);
+ }
+ } catch (Exception ex){
+ System.err.println(ex);
+ }
+ } else {
+ System.err.println("Invalid command: " + command);
+ }
+ }
+ };
+ }
+
+ void setIntegration(int frames) {
+ try {
+ if (integration != frames) {
+ integration = frames;
+ if (camera != null) {
+ if (Math.abs(integration) > 1) {
+ renderer.setDevice(new ImageIntegrator(integration));
+ } else {
+ renderer.setDevice(camera);
+ }
+ synchronized (imageBuffer) {
+ currentFrame = null;
+ imageBuffer.clear();
+ }
+ }
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (App.hasArgument("ct")) {
+ boolean direct = App.getArgumentValue("ct").equals("0") || App.getArgumentValue("ct").equalsIgnoreCase("false");
+ buttonServer.setSelected(!direct);
+ buttonDirect.setSelected(direct);
+ }
+ if (App.hasArgument("console")) {
+ console.start();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ try {
+ if (camera != null) {
+ camera.close();
+ camera = null;
+ server = null;
+ updateButtons();
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ try {
+ if (console != null) {
+ console.stop();
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ super.onStop();
+ }
+
+ //Overridable callbacks
+ @Override
+ public void onInitialize(int runCount) {
+ comboCameras.setEnabled(false);
+ comboType.setEnabled(false);
+ if (App.hasArgument("s")) {
+ renderer.setDevice((Source) getDevice("image"));
+ renderer.setAutoScroll(true);
+ ((Source) getDevice("image")).addListener(new ImageListener() {
+ @Override
+ public void onImage(Object o, BufferedImage bi, Data data) {
+ manageFit(bi, data);
+ manageUserOverlays(bi, data);
+ }
+
+ @Override
+ public void onError(Object o, Exception ex) {
+ }
+ }
+ );
+
+ } else {
+ usingServer = buttonServer.isSelected();
+ updateCameraList();
+ comboCameras.setEnabled(true);
+ comboType.setEnabled(true);
+ setComboCameraSelection(null);
+ setComboTypeSelection("All");
+
+ if (comboCameras.getModel().getSize() > 0) {
+ try {
+ if (App.hasArgument("cam")) {
+ setComboCameraSelection(App.getArgumentValue("cam"));
+ comboCamerasActionPerformed(null);
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+ updateButtons();
+ startTimer(1000);
+ }
+
+ boolean isVisible(String camera) {
+ return ((comboType.getSelectedIndex() == 0) || (getCameraType(camera).equals(comboType.getSelectedItem())));
+ }
+
+ DefaultComboBoxModel getCameraList(boolean fromServer) throws Exception {
+ DefaultComboBoxModel model = new DefaultComboBoxModel();
+ if (fromServer) {
+ try (PipelineServer srv = newServer()) {
+ srv.initialize();
+ List cameras = srv.getCameras();
+ Collections.sort(cameras);
+ for (String camera : cameras) {
+ if (isVisible(camera)) {
+ model.addElement(camera);
+ }
+ }
+ }
+
+ } else {
+ ArrayList cameras = (ArrayList) getContext().getClassByName("SfCamera").getMethod("getCameras", new Class[]{}).invoke(null);
+ for (String camera : cameras) {
+ if (isVisible(camera)) {
+ model.addElement(camera);
+ }
+ }
+ }
+ if (App.hasArgument("cam")) {
+ String camera = App.getArgumentValue("cam");
+ if (model.getIndexOf(camera) < 0) {
+ if (isVisible(camera)) {
+ model.addElement(camera);
+ }
+ }
+ }
+ model.addElement("");
+
+ return model;
+ }
+
+ PipelineServer newServer() throws IOException {
+ if (serverUrl != null) {
+ System.out.println("Connecting to server: " + serverUrl);
+ server = new PipelineServer(CAMERA_DEVICE_NAME, serverUrl);
+ } else {
+ System.out.println("Connecting to server");
+ server = new PipelineServer(CAMERA_DEVICE_NAME);
+ }
+ updateButtons();
+ return server;
+ }
+
+ boolean updatingCameraSelection;
+
+ void setComboCameraSelection(Object selection) {
+ updatingCameraSelection = true;
+ try {
+ comboCameras.setSelectedItem(selection);
+ } finally {
+ updatingCameraSelection = false;
+ }
+ }
+
+ void setComboTypeSelection(Object selection) {
+ updatingCameraSelection = true;
+ try {
+ comboType.setSelectedItem(selection);
+ } finally {
+ updatingCameraSelection = false;
+ }
+ }
+ boolean usingServer;
+
+ void updateCameraList() {
+ try {
+ String selected = (String) comboCameras.getSelectedItem();
+ comboCameras.setModel(getCameraList(usingServer));
+ if (selected != null) {
+ if (((DefaultComboBoxModel) comboCameras.getModel()).getIndexOf(camera) < 0) {
+ setComboCameraSelection(selected);
+ }
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ } finally {
+ updateButtons();
+ }
+ }
+
+ final Object lockOverlays = new Object();
+
+ void manageFit(BufferedImage bi, Data data) {
+ Overlay[][] fo = null;
+ if ((showFit || showProfile)) {
+ try {
+ fo = getFitOverlays(data);
+ } catch (Exception ex) {
+ System.err.println(ex);
+ }
+ }
+ synchronized (lockOverlays) {
+ fo = (fo == null) ? new Overlay[][]{null, null} : fo;
+ renderer.updateOverlays(fo[0], profileOv);
+ profileOv = fo[0];
+ renderer.updateOverlays(fo[1], fitOv);
+ fitOv = fo[1];
+ }
+ }
+
+ void manageUserOverlays(BufferedImage bi, Data data) {
+ Overlay[] fo = (bi == null) ? null : getUserOverlays(data);
+ synchronized (lockOverlays) {
+ renderer.updateOverlays(fo, userOv);
+ userOv = fo;
+ }
+ }
+
+ void manageTitleOverlay() {
+ Overlay to = null;
+ if ((buttonTitle.isSelected()) && (cameraName != null)) {
+ Font font = new Font("Arial", Font.PLAIN, 28);
+ to = new Text(renderer.getPenErrorText(), cameraName, font, new Point(-SwingUtils.getTextSize(cameraName, renderer.getGraphics().getFontMetrics(font)).width - 14, 26));
+ to.setFixed(true);
+ to.setAnchor(Overlay.ANCHOR_VIEWPORT_OR_IMAGE_TOP_RIGHT);
+ }
+
+ synchronized (lockOverlays) {
+ renderer.updateOverlays(to, titleOv);
+ titleOv = to;
+ }
+ }
+
+ @Override
+ public void onStateChange(State state, State former) {
+
+ }
+
+ @Override
+ public void onExecutedFile(String fileName, Object result) {
+ }
+
+ //Callback to perform update - in event thread
+ @Override
+ protected void doUpdate() {
+ }
+
+ Thread devicesInitTask;
+
+ void setCamera(String cameraName) throws IOException, InterruptedException {
+ System.out.println("Initializing: " + cameraName);
+ parseUserOverlays();
+ errorOverlay = null;
+ lastMarkerPos = null;
+ lastFrame = null;
+ lastPipelinePars = null;
+
+ if (dataTableDialog != null) {
+ dataTableDialog.dispose();
+ dataTableDialog = null;
+ }
+ dataTableModel = null;
+
+ if (calibrationDialolg != null) {
+ calibrationDialolg.dispose();
+ calibrationDialolg = null;
+ }
+
+ boolean was_server = false;
+ if (camera != null) {
+ //camera.removeAllListeners();
+ was_server = (server != null);
+ camera.close();
+ camera = null;
+ server = null;
+ }
+ updateButtons();
+ instanceName = null;
+ renderer.setDevice(null);
+ renderer.setShowReticle(false);
+ renderer.removeOverlays(fitOv);
+ renderer.removeOverlays(profileOv);
+ renderer.removeOverlays(userOv);
+ renderer.clear();
+ renderer.resetZoom();
+
+ boolean changed = !String.valueOf(cameraName).equals(this.cameraName);
+ this.cameraName = cameraName;
+
+ if (changed || buttonDirect.isSelected()) {
+ spinnerThreshold.setVisible(false);
+ checkThreshold.setEnabled(false);
+ checkGoodRegion.setEnabled(false);
+ setGoodRegionOptionsVisible(false);
+ setSlicingOptionsVisible(false);
+ }
+ synchronized (imageBuffer) {
+ currentFrame = null;
+ imageBuffer.clear();
+ }
+ if (changed) {
+ checkBackground.setEnabled(false);
+ if ((devicesInitTask != null) && (devicesInitTask.isAlive())) {
+ devicesInitTask.interrupt();
+ }
+ if (screen != null) {
+ screen.close();
+ screen = null;
+ }
+ if (filter != null) {
+ filter.close();
+ filter = null;
+ }
+ if (renderer.isPaused()) {
+ renderer.resume();
+ removePauseOverlay();
+ pauseSelection.setVisible(false);
+ panelCameraSelection.setVisible(true);
+ }
+ }
+ manageTitleOverlay();
+ if (App.isDetached()) {
+ getTopLevel().setTitle(cameraName == null ? "ScreenPanel" : cameraName);
+ }
+ if (cameraName == null) {
+ return;
+ }
+
+ System.out.println("Setting camera: " + cameraName + " [" + (buttonServer.isSelected() ? "server" : "direct") + "]");
+ try {
+ if (buttonServer.isSelected()) {
+ camera = newServer();
+ camera.getConfig().flipHorizontally = false;
+ camera.getConfig().flipVertically = false;
+ camera.getConfig().rotation = 0.0;
+ camera.getConfig().roiX = 0;
+ camera.getConfig().roiY = 0;
+ camera.getConfig().roiWidth = -1;
+ camera.getConfig().roiHeight = -1;
+ } else {
+ //camera = new SfCamera(CAMERA_DEVICE_NAME, cameraName);
+ camera = (ColormapSource) getContext().getClassByName("SfCamera").getConstructor(new Class[]{String.class, String.class}).newInstance(new Object[]{CAMERA_DEVICE_NAME, cameraName});
+ }
+ camera.initialize();
+ camera.assertInitialized();
+ System.out.println("Camera initialization OK");
+ if (server != null) {
+ //server.start(cameraName, false);
+ String pipelineName = cameraName + pipelineSuffix;
+ instanceName = cameraName + pipelineSuffix + "1";
+ if (!server.getPipelines().contains(pipelineName)) {
+ System.out.println("Creating pipeline: " + pipelineName);
+ HashMap config = new HashMap<>();
+ config.put("camera_name", cameraName);
+ //server.createFromConfig(config, pipelineName);
+ server.savePipelineConfig(pipelineName, config);
+ }
+ server.start(pipelineName, instanceName);
+
+ updatePipelineControls();
+ checkThreshold.setEnabled(true);
+ checkGoodRegion.setEnabled(true);
+ } else {
+ checkThreshold.setSelected(false);
+ checkGoodRegion.setSelected(false);
+ if (polling <= 0) {
+ camera.setMonitored(true);
+ } else {
+ camera.setPolling(polling);
+ }
+ camera.setBackgroundEnabled(checkBackground.isSelected());
+ }
+ updateButtons();
+ camera.getConfig().save();
+ if (Math.abs(integration) > 1) {
+ renderer.setDevice(new ImageIntegrator(integration));
+ } else {
+ renderer.setDevice(camera);
+ }
+ renderer.setAutoScroll(true);
+ //renderer.setMarker(marker);
+ clearMarker();
+ imageSize = null;
+
+ camera.addListener(new ImageListener() {
+ @Override
+ public void onImage(Object o, BufferedImage bi, Data data) {
+ if (bi != null) {
+ if ((imageSize == null) || imageSize.width != bi.getWidth() || imageSize.height != bi.getHeight()) {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ if ((renderer.getMode() == RendererMode.Zoom) || (renderer.getMode() == RendererMode.Fixed)) {
+ centralizeRenderer();
+ }
+ checkReticle();
+ }
+ });
+ imageSize = new Dimension(bi.getWidth(), bi.getHeight());
+ }
+ renderer.setProfileSize(Math.min(bi.getWidth(), bi.getHeight()));
+ }
+ //renderer.setCalibration(camera.getCalibration());
+ if (!renderer.isPaused()) {
+ if (data != null) {
+ synchronized (imageBuffer) {
+ currentFrame = new Frame(data);
+ if (imageBufferLenght >= 1) {
+ imageBuffer.add(currentFrame);
+ if (imageBuffer.size() > imageBufferLenght) {
+ imageBuffer.remove(0);
+ setBufferFull(true);
+ } else {
+ setBufferFull(false);
+ }
+ } else {
+ setBufferFull(true);
+ }
+ //Update data
+ if (!renderer.isPaused()) {
+ updateStreamData();
+ }
+ updateMarker();
+ }
+ }
+ manageFit(bi, data);
+ manageUserOverlays(bi, data);
+ }
+ //updateImageData();
+ }
+
+ @Override
+ public void onError(Object o, Exception ex) {
+ //System.err.println(ex);
+ }
+ });
+
+ } catch (Exception ex) {
+ showException(ex);
+ renderer.clearOverlays();
+ updatePipelineControls();
+ if (renderer.getDevice() == null) {
+ //renderer.setZoom(1.0);
+ //renderer.setMode(RendererMode.Zoom);
+ errorOverlay = new Text(renderer.getPenErrorText(), ex.toString(), new Font("Verdana", Font.PLAIN, 12), new Point(20, 20));
+ errorOverlay.setFixed(true);
+ errorOverlay.setAnchor(Overlay.ANCHOR_VIEWPORT_TOP_LEFT);
+ renderer.addOverlay(errorOverlay);
+ }
+ } finally {
+ //checkReticle();
+ onTimer();
+ }
+ onChangeColormap(null);
+ checkBackground.setEnabled(true);
+ if (changed) {
+ boolean electrons = getCameraType(cameraName).equals(ELECTRONS_TYPE);
+ comboScreen.setModel(new DefaultComboBoxModel());
+ comboScreen.setEnabled(false);
+ comboFilter.setModel(new DefaultComboBoxModel());
+ comboFilter.setEnabled(false);
+ panelFilter.setVisible(electrons);
+ panelScreen.setVisible(electrons);
+ if (electrons) {
+ //Parallelizing initialization
+ devicesInitTask = new Thread(() -> {
+ try {
+ if (cameraName.contains("DSRM")) {
+ screen = new DiscretePositioner("CurrentScreen", cameraName + ":POSITION_SP", cameraName + ":POSITION");
+ } else {
+ screen = new DiscretePositioner("CurrentScreen", cameraName + ":SET_SCREEN1_POS", cameraName + ":GET_SCREEN1_POS");
+ }
+ screen.setMonitored(true);
+ screen.initialize();
+ DefaultComboBoxModel model = new DefaultComboBoxModel();
+ for (String pos : screen.getPositions()) {
+ model.addElement(pos);
+ }
+ comboScreen.setModel(model);
+ comboScreen.setSelectedItem(screen.read());
+
+ } catch (Exception ex) {
+ comboScreen.setModel(new DefaultComboBoxModel());
+ System.err.println(ex.getMessage());
+ screen = null;
+ }
+ comboScreen.setEnabled(screen != null);
+ valueScreen.setDevice(screen);
+
+ try {
+ filter = new DiscretePositioner("CurrentFilter", cameraName + ":SET_FILTER", cameraName + ":GET_FILTER");
+ filter.setMonitored(true);
+ filter.initialize();
+ DefaultComboBoxModel model = new DefaultComboBoxModel();
+ for (String pos : filter.getPositions()) {
+ model.addElement(pos);
+ }
+ comboFilter.setModel(model);
+ comboFilter.setSelectedItem(filter.read());
+ } catch (Exception ex) {
+ System.err.println(ex.getMessage());
+ filter = null;
+ }
+ comboFilter.setEnabled(filter != null);
+ valueFilter.setDevice(filter);
+ });
+ devicesInitTask.start();
+ }
+ }
+ }
+
+ class ImageIntegrator extends ColormapSource {
+
+ ImageIntegrator(int num) {
+ super("Image Averager", camera.getConfig());
+ boolean continuous = (num < 0);
+ final int numImages = Math.abs(num);
+
+ camera.addListener(new ImageListener() {
+ final ArrayList buffer = new ArrayList();
+ Data integration = null;
+
+ @Override
+ public void onImage(Object o, BufferedImage bi, Data data) {
+ try{
+ if (continuous) {
+ buffer.add(data);
+ if (buffer.size() >= numImages) {
+ for (Data d : buffer) {
+ process(d);
+ }
+ }
+ } else {
+ buffer.add(null); //Just to count
+ process(data);
+ }
+ }catch (Exception ex){
+ buffer.clear();
+ integration = null;
+ ImageIntegrator.this.pushData(null);
+ ex.printStackTrace();
+ return;
+ }
+ if (buffer.size() >= numImages) {
+ if (continuous) {
+ buffer.remove(0);
+ } else {
+ buffer.clear();
+ }
+ if (integration != null) {
+ //integration.div(numImages);
+ ImageIntegrator.this.pushData(integration);
+ }
+ integration = null;
+ }
+ }
+
+ void process(Data data) {
+ if (integration == null) {
+ integration = new Data(data);
+ } else {
+ integration.sum(data);
+ }
+ }
+
+ @Override
+ public void onError(Object origin, Exception ex) {
+ }
+ });
+
+ }
+ }
+
+ boolean bufferFull = true;
+
+ void setBufferFull(boolean value){
+ if (value != bufferFull){
+ SwingUtilities.invokeLater(()->{
+ buttonPause.setBackground(value ? buttonSave.getBackground() : buttonSave.getBackground().brighter());
+ });
+ bufferFull = value;
+ }
+ }
+
+ volatile Dimension imageSize;
+
+ void checkReticle() {
+ if ((renderer.getDevice() != null) && (camera != null) && (camera.getConfig().isCalibrated()) && buttonReticle.isSelected()) {
+ //renderer.setCalibration(camera.getCalibration());
+ renderer.configureReticle(new Dimension(800, 800), 200);
+ renderer.setShowReticle(true);
+ } else {
+ //renderer.setCalibration(null);
+ renderer.setShowReticle(false);
+ }
+ renderer.refresh();
+ }
+
+ void checkMarker(Point p) throws IOException {
+ if (camera != null) {
+ if (buttonMarker.isSelected()) {
+ Dimension d = renderer.getImageSize();
+ if (p==null){
+ p = (d == null) ? new Point(renderer.getWidth() / 2, renderer.getHeight() / 2) : new Point(d.width / 2, d.height / 2);
+ }
+ Overlay ov = null;
+ marker = new Overlays.Crosshairs(renderer.getPenMarker(), p, new Dimension(100, 100));
+ marker.setMovable(true);
+ marker.setPassive(false);
+ } else {
+ marker = null;
+ }
+ renderer.setMarker(marker);
+ onMarkerChanged();
+ }
+ }
+
+ Point lastMarkerPos;
+ void onMarkerChanged() throws IOException {
+ lastMarkerPos = getStreamMarkerPos();
+ if (marker == null) {
+ setInstanceConfigValue("Marker", null);
+ } else {
+ setInstanceConfigValue("Marker", new int[]{marker.getPosition().x, marker.getPosition().y});
+ }
+ }
+
+ void updateMarker() {
+ try {
+ if (server != null) {
+ Point p = getStreamMarkerPos();
+ if (p != null) {
+ //To prevent a local change being overriden by a message having the old settings.
+ //TODO: This is not bullet-proof, as one can have 2 changes between 2 frames...
+ if (!p.equals(lastMarkerPos)){
+ if (p.x == Integer.MIN_VALUE) {
+ if (buttonMarker.isSelected()) {
+ buttonMarker.setSelected(false);
+ checkMarker(null);
+ }
+ } else {
+ if (!buttonMarker.isSelected()) {
+ buttonMarker.setSelected(true);
+ checkMarker(p);
+ } else {
+ if (!p.equals(marker.getPosition())){
+ marker.setPosition(p);
+ }
+ }
+ }
+ }
+ }
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ Point getStreamMarkerPos() throws IOException{
+ //System.out.println(server.getInstanceConfig().get("Marker"));
+ Map pars = server.getProcessingParameters();
+ if (pars != null) {
+ List markerPosition = (List) pars.get("Marker");
+ if (markerPosition != null) {
+ return new Point((Integer)markerPosition.get(0), (Integer)markerPosition.get(1));
+ }
+ return new Point(Integer.MIN_VALUE,Integer.MIN_VALUE);
+ }
+ return null;
+ }
+ void clearMarker(){
+ marker = null;
+ renderer.setMarker(marker);
+ }
+
+ void setInstanceConfigValue(String name, Object value) throws IOException {
+ if (server != null) {
+ Map map = server.getInstanceConfig();
+ map.put(name, value);
+ server.setInstanceConfig(map);
+ }
+ }
+
+ void updateZoom() {
+ try {
+ buttonZoomStretch.setSelected(renderer.getMode() == RendererMode.Stretch);
+ buttonZoomFit.setSelected(renderer.getMode() == RendererMode.Fit);
+ if (renderer.getMode() == RendererMode.Fixed) {
+ buttonZoomNormal.setSelected(true);
+ } else if (renderer.getMode() == RendererMode.Zoom) {
+ if (renderer.getZoom() == 1) {
+ buttonZoomNormal.setSelected(true);
+ } else if (renderer.getZoom() == 0.5) {
+ buttonZoom05.setSelected(true);
+ } else if (renderer.getZoom() == 0.25) {
+ buttonZoom025.setSelected(true);
+ } else if (renderer.getZoom() == 2.0) {
+ buttonZoom2.setSelected(true);
+ } else {
+ buttonGroup1.clearSelection();
+ }
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ boolean updatingColormap;
+
+ void updateColormap() {
+ updatingColormap = true;
+ try {
+ if ((camera != null) && (camera instanceof ColormapSource)) {
+ ColormapSourceConfig config = ((ColormapSource) camera).getConfig();
+ comboColormap.setSelectedItem(config.colormap);
+ if (config.isDefaultColormap()) {
+ buttonFullRange.setSelected(true);
+ } else if (config.colormapAutomatic) {
+ buttonAutomatic.setSelected(true);
+ } else {
+ buttonManual.setSelected(true);
+ }
+ btFixColormapRange.setVisible(buttonAutomatic.isSelected());
+ spinnerMin.setEnabled(buttonManual.isSelected());
+ spinnerMax.setEnabled(buttonManual.isSelected());
+ if (!Double.isNaN(config.colormapMin)) {
+ spinnerMin.setValue(Math.min(Math.max((int) config.colormapMin, 0), 65535));
+ }
+ if (!Double.isNaN(config.colormapMax)) {
+ spinnerMax.setValue(Math.min(Math.max((int) config.colormapMax, 0), 65535));
+ }
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ updatingColormap = false;
+ }
+
+ void updatePipelineProperties(){
+ goodRegion = checkGoodRegion.isSelected();
+ spinnerThreshold.setVisible(checkThreshold.isSelected());
+ setGoodRegionOptionsVisible(goodRegion);
+ slicing = goodRegion && checkSlicing.isSelected();
+ setSlicingOptionsVisible(slicing);
+ updatingServerControls = false;
+ }
+
+ boolean updatingServerControls;
+
+ void updatePipelineControls() {
+ if (server != null) {
+ updatingServerControls = true;
+ if (server.isStarted()){
+ try {
+ checkBackground.setSelected(server.getBackgroundSubtraction());
+ Double threshold = (server.getThreshold());
+ checkThreshold.setSelected(threshold != null);
+ spinnerThreshold.setValue((threshold == null) ? 0 : threshold);
+ Map gr = (server.getGoodRegion());
+ checkGoodRegion.setSelected(gr != null);
+ if (gr != null) {
+ spinnerGrThreshold.setValue(((Number) gr.get("threshold")).doubleValue());
+ spinnerGrScale.setValue(((Number) gr.get("gfscale")).doubleValue());
+ }
+ Map slicing = (server.getSlicing());
+ checkSlicing.setSelected(slicing != null);
+ if (slicing != null) {
+ spinnerSlNumber.setValue(((Number) slicing.get("number_of_slices")).intValue());
+ spinnerSlScale.setValue(((Number) slicing.get("scale")).doubleValue());
+ spinnerSlOrientation.setValue((String) slicing.get("orientation"));
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ updatePipelineProperties();
+ }
+ }
+
+ boolean changedPipelinePars(Map pars1, Map pars2) {
+ String[] keys = new String[]{"image_background_enable", "image_threshold", "image_good_region",
+ "threshold", "gfscale", "image_slices", "number_of_slices", "scale", "orientation"};
+ for (String key:keys){
+ Object o1 = pars1.get(key);
+ Object o2 = pars2.get(key);
+ if (o1==null){
+ if (o2!=null){
+ return true;
+ }
+ } else if (!o1.equals(o2)){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void updatePipelineControls(Map pars) {
+ if (pars!=null){
+ updatingServerControls = true;
+ try {
+ boolean background = (boolean) pars.get("image_background_enable");
+ checkBackground.setSelected(background);
+ Double threshold = (Double) (pars.get("image_threshold"));
+ checkThreshold.setSelected(threshold != null);
+ spinnerThreshold.setValue((threshold == null) ? 0 : threshold);
+ Map gr = (Map) pars.get("image_good_region");
+ checkGoodRegion.setSelected(gr != null);
+ if (gr != null) {
+ Double value = ((Number) gr.get("threshold")).doubleValue();
+ spinnerGrThreshold.setValue(value);
+ Double scale = ((Number) gr.get("gfscale")).doubleValue();
+ spinnerGrScale.setValue(scale);
+ }
+ Map slicing = (Map) (pars.get("image_slices"));
+ checkSlicing.setSelected(slicing != null);
+ if (slicing != null) {
+ int slices = ((Number) slicing.get("number_of_slices")).intValue();
+ spinnerSlNumber.setValue(slices);
+ double scale = ((Number) slicing.get("scale")).doubleValue();
+ spinnerSlScale.setValue(scale);
+ String orientation = (String) slicing.get("orientation");
+ spinnerSlOrientation.setValue(orientation);
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ updatePipelineProperties();
+ }
+ }
+
+ void setGoodRegionOptionsVisible(boolean visible) {
+ spinnerGrThreshold.setVisible(visible);
+ labelGrThreshold.setVisible(visible);
+ spinnerGrScale.setVisible(visible);
+ labelGrScale.setVisible(visible);
+ panelSlicing.setVisible(visible);
+ }
+
+ void setSlicingOptionsVisible(boolean visible) {
+ spinnerSlNumber.setVisible(visible);
+ labelSlNumber.setVisible(visible);
+ spinnerSlScale.setVisible(visible);
+ labelSlScale.setVisible(visible);
+ spinnerSlOrientation.setVisible(visible);
+ labelSlOrientation.setVisible(visible);
+ }
+
+ boolean isCameraStopped() {
+ if ((server != null) && !server.isStarted()) {
+ return true;
+ }
+ return ((camera == null) || camera.isClosed());
+ }
+
+ boolean updatingButtons;
+
+ void updateButtons() {
+ updatingButtons = true;
+ try {
+ boolean active = !isCameraStopped();//(camera != null);
+ buttonSave.setEnabled(active);
+ buttonGrabBackground.setEnabled(active);
+ buttonMarker.setEnabled(active);
+ buttonProfile.setEnabled(active);
+ buttonFit.setEnabled(active);
+ buttonReticle.setEnabled(active && camera.getConfig().isCalibrated());
+ buttonStreamData.setEnabled(active && (server != null));
+ buttonPause.setEnabled(active);
+
+ if (renderer.isPaused() != buttonPause.isSelected()) {
+ buttonPause.setSelected(renderer.isPaused());
+ buttonPauseActionPerformed(null);
+ }
+ if (renderer.getShowReticle() != buttonReticle.isSelected()) {
+ //buttonReticle.setSelected(renderer.getShowReticle());
+ }
+ if ((renderer.getMarker() == null) && buttonMarker.isSelected()) {
+ buttonMarker.setSelected(false);
+ }
+ buttonSave.setSelected(renderer.isSnapshotDialogVisible());
+
+ } finally {
+ updatingButtons = false;
+ }
+ }
+
+ Frame lastFrame = null;
+ Map lastPipelinePars = null;
+
+
+ @Override
+ protected void onTimer() {
+ for (Device dev : new Device[]{screen, filter}) {
+ if (dev != null) {
+ dev.request();
+ }
+ }
+
+ textState.setText((camera == null) ? "" : camera.getState().toString());
+ if (App.hasArgument("s")) {
+ try {
+ ((Source) getDevice("image")).initialize();
+ } catch (IOException ex) {
+ Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
+ } catch (InterruptedException ex) {
+ Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ updateZoom();
+ updateColormap();
+ updateButtons();
+ checkHistogram.setSelected((histogramDialog != null) && (histogramDialog.isShowing()));
+
+ try{
+ Frame frame = getCurrentFrame();
+ if (frame!=lastFrame){
+ lastFrame = frame;
+ if (frame!=null){
+ Map pars = getProcessingParameters(frame.cache);
+ if((lastPipelinePars==null) || changedPipelinePars(pars, lastPipelinePars)){
+ //System.out.println("Update pipeline: " + pars);
+ lastPipelinePars = pars;
+ updatePipelineControls(pars);
+ }
+ }
+ }
+ } catch (Exception ex){
+ ex.printStackTrace();
+ }
+ }
+
+ Pen penFit = new Pen(new Color(192, 105, 0), 0);
+ Pen penCross = new Pen(new Color(192, 105, 0), 0);
+ Pen penSlices = new Pen(Color.CYAN.darker(), 1);
+
+ Frame getCurrentFrame() {
+ if ((imageBufferLenght > 1) && (renderer.isPaused())) {
+ int index = ((int) pauseSelection.getValue()) - 1;
+ synchronized (imageBuffer) {
+ return (index < imageBuffer.size()) ? imageBuffer.get(index) : null;
+ }
+ }
+ return currentFrame;
+ }
+
+ Frame getFrame(Data data) {
+ synchronized (imageBuffer) {
+ for (Frame f : imageBuffer) {
+ if (f.data == data) {
+ return f;
+ }
+ }
+ }
+ return null;
+ }
+
+ void setImageBufferSize(int size) {
+ if (renderer.isPaused()) {
+ throw new RuntimeException("Cannot change buffer size whn paused");
+ }
+ synchronized (imageBuffer) {
+ imageBufferLenght = size;
+ imageBuffer.clear();
+ }
+
+ }
+
+ Overlay[][] getFitOverlays(Data data) {
+ Overlays.Polyline hgaussian = null;
+ Overlays.Polyline vgaussian = null;
+ Overlays.Polyline hprofile = null;
+ Overlays.Polyline vprofile = null;
+ Double xMean = null, xSigma = null, xNorm = null, xCom = null, xRms = null;
+ Double yMean = null, ySigma = null, yNorm = null, yCom = null, yRms = null;
+ double[] pX = null, pY = null, gX = null, gY = null;
+ PointDouble[] sliceCenters = null;
+ if (data != null) {
+ int height = data.getHeight();
+ int width = data.getWidth();
+ int profileSize = renderer.getProfileSize();
+ if ((useServerStats) && (server != null)) {
+ try {
+
+ ImageData id = getFrame(data);
+ if (id == null) {
+ return null;
+ }
+ xMean = id.x_fit_mean;
+ xSigma = id.x_fit_standard_deviation;
+ yMean = id.y_fit_mean;
+ ySigma = id.y_fit_standard_deviation;
+ gX = id.x_fit_gauss_function;
+ gY = id.y_fit_gauss_function;
+ pX = id.x_profile;
+ pY = id.y_profile;
+ xCom = id.x_center_of_mass;
+ xRms = id.x_rms;
+ yCom = id.y_center_of_mass;
+ yRms = id.y_rms;
+ sliceCenters = id.sliceCenters;
+
+ profileSize /= 4;
+ if (pX != null) {
+ int[] xp = Arr.indexesInt(pX.length);
+ int[] xg = xp;
+ int[] yp = new int[pX.length];
+ int[] yg = new int[pX.length];
+
+ List l = Arrays.asList((Double[]) Convert.toWrapperArray(pX));
+ double minProfile = Collections.min(l);
+ double maxProfile = Collections.max(l);
+ double rangeProfile = maxProfile - minProfile;
+ double minGauss = minProfile;
+ double rangeGauss = rangeProfile;
+ //If not good region, range of profile and fit are similar so save this calcultion
+ if (goodRegion && id.gr_size_x > 0) {
+ l = Arrays.asList((Double[]) Convert.toWrapperArray(Arrays.copyOfRange(gX, id.gr_pos_x, id.gr_pos_x + id.gr_size_x)));
+ minGauss = Collections.min(l);
+ rangeGauss = Collections.max(l) - minGauss;
+ }
+
+ for (int i = 0; i < xp.length; i++) {
+ if (gX != null) {
+ yg[i] = (int) (height - 1 - (((gX[i] - minGauss) / rangeGauss) * profileSize));
+ }
+ yp[i] = (int) (height - 1 - (((pX[i] - minProfile) / rangeProfile) * profileSize));
+ }
+
+ if (goodRegion && id.gr_size_x > 0) {
+ xg = Arrays.copyOfRange(xg, id.gr_pos_x, id.gr_pos_x + id.gr_size_x);
+ yg = Arrays.copyOfRange(yg, id.gr_pos_x, id.gr_pos_x + id.gr_size_x);
+ }
+
+ vgaussian = new Overlays.Polyline(penFit, xg, yg);
+ vprofile = new Overlays.Polyline(renderer.getPenProfile(), xp, yp);
+ }
+
+ if (pY != null) {
+ int[] xp = new int[pY.length];
+ int[] xg = new int[pY.length];
+ int[] yp = Arr.indexesInt(pY.length);
+ int[] yg = yp;
+
+ List l = Arrays.asList((Double[]) Convert.toWrapperArray(pY));
+ double minProfile = Collections.min(l);
+ double maxProfile = Collections.max(l);
+ double rangeProfile = maxProfile - minProfile;
+ double minGauss = minProfile;
+ double rangeGauss = rangeProfile;
+ //If not good region, range of profile and fit are similar so save this calcultion
+ if (goodRegion && id.gr_size_y > 0) {
+ l = Arrays.asList((Double[]) Convert.toWrapperArray(Arrays.copyOfRange(gY, id.gr_pos_y, id.gr_pos_y + id.gr_size_y)));
+ minGauss = Collections.min(l);
+ rangeGauss = Collections.max(l) - minGauss;
+ }
+
+ for (int i = 0; i < xp.length; i++) {
+ if (gY != null) {
+ xg[i] = (int) (((gY[i] - minGauss) / rangeGauss) * profileSize);
+ }
+ xp[i] = (int) (((pY[i] - minProfile) / rangeProfile) * profileSize);
+ }
+
+ if (goodRegion && id.gr_size_x > 0) {
+ xg = Arrays.copyOfRange(xg, id.gr_pos_y, id.gr_pos_y + id.gr_size_y);
+ yg = Arrays.copyOfRange(yg, id.gr_pos_y, id.gr_pos_y + id.gr_size_y);
+ }
+ hgaussian = new Overlays.Polyline(penFit, xg, yg);
+ hprofile = new Overlays.Polyline(renderer.getPenProfile(), xp, yp);
+ }
+ } catch (Exception ex) {
+ System.err.println(ex.getMessage());
+ return null;
+ }
+ } else {
+ ArrayProperties properties = data.getProperties();
+ double maxPlot = properties.max;
+ double minPlot = properties.min;
+ double rangePlot = maxPlot - minPlot;
+
+ if (rangePlot <= 0) {
+ return null;
+ }
+ if (renderer.getCalibration() != null) {
+ try {
+ double[] sum = data.integrateVertically(true);
+ double[] saux = new double[sum.length];
+ int[] p = new int[sum.length];
+ double[] x_egu = renderer.getCalibration().getAxisX(sum.length);
+ double[] comRms = getComRms(sum, x_egu);
+ xCom = comRms[0];
+ xRms = comRms[1];
+ int[] x = Arr.indexesInt(sum.length);
+ DescriptiveStatistics stats = new DescriptiveStatistics(sum);
+ double min = stats.getMin();
+ for (int i = 0; i < sum.length; i++) {
+ saux[i] = sum[i] - min;
+ }
+ if (showFit) {
+ double[] gaussian = fitGaussian(saux, x);
+ if (gaussian != null) {
+ if ((gaussian[2] < sum.length * 0.45)
+ && (gaussian[2] > 2)
+ && (gaussian[0] > min * 0.03)) {
+ xNorm = gaussian[0];
+ xMean = gaussian[1];
+ xSigma = gaussian[2];
+ double[] fit = getFitFunction(gaussian, x);
+ int[] y = new int[x.length];
+ for (int i = 0; i < x.length; i++) {
+ y[i] = (int) (height - 1 - ((((fit[i] + min) / height - minPlot) / rangePlot) * profileSize));
+ }
+ vgaussian = new Overlays.Polyline(penFit, x, y);
+ }
+ }
+ }
+ if (showProfile) {
+ for (int i = 0; i < x.length; i++) {
+ p[i] = (int) (height - 1 - (((sum[i] / height - minPlot) / rangePlot) * profileSize));
+ }
+ vprofile = new Overlays.Polyline(renderer.getPenProfile(), x, p);
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+
+ try {
+ double[] sum = data.integrateHorizontally(true);
+ double[] saux = new double[sum.length];
+ int[] p = new int[sum.length];
+ double[] y_egu = renderer.getCalibration().getAxisY(sum.length);
+ double[] comRms = getComRms(sum, y_egu);
+ yCom = comRms[0];
+ yRms = comRms[1];
+ int[] x = Arr.indexesInt(sum.length);
+ DescriptiveStatistics stats = new DescriptiveStatistics(sum);
+ double min = stats.getMin();
+ for (int i = 0; i < sum.length; i++) {
+ saux[i] = sum[i] - min;
+ }
+
+ if (showFit) {
+ double[] gaussian = fitGaussian(saux, x);
+ if (gaussian != null) {
+ //Only aknowledge beam fully inside the image and peak over 3% of min
+ if ((gaussian[2] < sum.length * 0.45)
+ && (gaussian[2] > 2)
+ && (gaussian[0] > min * 0.03)) {
+ yNorm = gaussian[0];
+ yMean = gaussian[1];
+ ySigma = gaussian[2];
+ double[] fit = getFitFunction(gaussian, x);
+
+ int[] y = new int[x.length];
+ for (int i = 0; i < x.length; i++) {
+ y[i] = (int) ((((fit[i] + min) / width - minPlot) / rangePlot) * profileSize);
+ }
+ hgaussian = new Overlays.Polyline(penFit, y, x);
+ }
+ }
+ }
+ if (showProfile) {
+ for (int i = 0; i < x.length; i++) {
+ p[i] = (int) (((sum[i] / width - minPlot) / rangePlot) * profileSize);
+ }
+ hprofile = new Overlays.Polyline(renderer.getPenProfile(), p, x);
+ }
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ if (xSigma != null) {
+ xSigma *= renderer.getCalibration().getScaleX();
+ }
+ if (ySigma != null) {
+ ySigma *= renderer.getCalibration().getScaleY();
+ }
+ if (xMean != null) {
+ xMean = data.getX((int) Math.round(xMean));
+ }
+ if (yMean != null) {
+ yMean = data.getY((int) Math.round(yMean));
+ }
+ }
+ }
+ final String units = (renderer.getCalibration() != null) ? "\u00B5m" : "px";
+ final String fmt = "%7.1f" + units;
+ Overlays.Text textCom = null;
+ Overlay[] pOv = null, fOv = null;
+ Font fontInfoText = new Font(Font.MONOSPACED, 0, 14);
+ Point textPosition = new Point(12, 20);
+ if (showProfile) {
+ if ((xCom != null) && (yCom != null)) {
+ String text = String.format("com x: m=" + fmt + " \u03C3=" + fmt + "\ncom y: m=" + fmt + " \u03C3=" + fmt, xCom, xRms, yCom, yRms);
+ textCom = new Overlays.Text(renderer.getPenProfile(), text, fontInfoText, textPosition);
+ textCom.setFixed(true);
+ textCom.setAnchor(Overlay.ANCHOR_VIEWPORT_TOP_LEFT);
+ }
+ pOv = new Overlay[]{hprofile, vprofile, textCom};
+ textPosition = new Point(textPosition.x, textPosition.y + 34);
+ }
+ if (showFit) {
+ Overlays.Crosshairs cross = null;
+ Overlays.Text textFit = null;
+ if ((xMean != null) && (yMean != null)) {
+ String text = String.format("fit x: m=" + fmt + " \u03C3=" + fmt + "\nfit y: m=" + fmt + " \u03C3=" + fmt, xMean, xSigma, yMean, ySigma);
+ textFit = new Overlays.Text(penFit, text, fontInfoText, textPosition);
+ textFit.setFixed(true);
+ textFit.setAnchor(Overlay.ANCHOR_VIEWPORT_TOP_LEFT);
+ Point center = new Point(xMean.intValue(), yMean.intValue());
+ if (renderer.getCalibration() != null) {
+ center = renderer.getCalibration().convertToImagePosition(new PointDouble(xMean, yMean));
+ xSigma /= renderer.getCalibration().getScaleX();
+ ySigma /= renderer.getCalibration().getScaleY();
+ }
+ cross = new Overlays.Crosshairs(penCross, center, new Dimension(Math.abs(2 * xSigma.intValue()), 2 * Math.abs(ySigma.intValue())));
+ }
+ textPosition = new Point(textPosition.x, textPosition.y + 34);
+ fOv = new Overlay[]{hgaussian, vgaussian, cross, textFit};
+
+ if (goodRegion) {
+ try {
+ double[] x = getServerDoubleArray("gr_x_axis");
+ double[] y = getServerDoubleArray("gr_y_axis");
+ double x1 = x[0];
+ double x2 = x[x.length - 1];
+ double y1 = y[0];
+ double y2 = y[y.length - 1];
+ Overlays.Rect goodRegionOv = new Overlays.Rect(new Pen(penFit.getColor(), 0, Pen.LineStyle.dotted));
+ goodRegionOv.setCalibration(renderer.getCalibration());
+ goodRegionOv.setAbsolutePosition(new PointDouble(x1, y1));
+ goodRegionOv.setAbsoluteSize(new DimensionDouble(x2 - x1, y2 - y1));
+ fOv = Arr.append(fOv, goodRegionOv);
+
+ if (slicing) {
+ if (sliceCenters != null) {
+ for (PointDouble sliceCenter : sliceCenters) {
+ Overlays.Crosshairs center = new Overlays.Crosshairs(penSlices);
+ center.setCalibration(renderer.getCalibration());
+ center.setAbsolutePosition(sliceCenter);
+ center.setSize(new Dimension(10, 10));
+ fOv = Arr.append(fOv, center);
+ }
+ if (sliceCenters.length > 1) {
+ double[] fit = fitPolynomial(sliceCenters, 1);
+ double angle = Math.toDegrees(Math.atan(fit[1]));
+ Overlays.Text text = new Overlays.Text(penSlices, String.format("slice: \u03B8= %5.1fdeg", angle), fontInfoText, textPosition);
+ text.setFixed(true);
+ text.setAnchor(Overlay.ANCHOR_VIEWPORT_TOP_LEFT);
+ fOv = Arr.append(fOv, text);
+ }
+ }
+ }
+ } catch (Exception ex) {
+ }
+ }
+
+ }
+ return new Overlay[][]{pOv, fOv};
+ }
+ return null;
+ }
+
+ class UserOverlay {
+
+ String name;
+ Overlay obj;
+ String[] channels;
+ }
+ ArrayList userOverlayConfig;
+
+ void parseUserOverlays() {
+ Properties userOverlays = new Properties();
+ userOverlayConfig = new ArrayList<>();
+ if (userOverlaysConfigFile != null) {
+ try {
+ try (FileInputStream in = new FileInputStream(getContext().getSetup().expandPath(userOverlaysConfigFile))) {
+ userOverlays.load(in);
+
+ for (String name : userOverlays.stringPropertyNames()) {
+ String val = userOverlays.getProperty(name);
+ try {
+ UserOverlay uo = new UserOverlay();
+ uo.name = name;
+ String type = val.substring(0, val.indexOf("(")).trim();
+ String pars = val.substring(val.indexOf("(") + 1, val.lastIndexOf(")")).trim();
+ String[] tokens = pars.split(",");
+ for (int i = 0; i < tokens.length; i++) {
+ tokens[i] = tokens[i].trim();
+ }
+ Color color = Color.GRAY;
+ try {
+ color = (Color) Color.class.getField(tokens[tokens.length - 1].toUpperCase()).get(null);
+ } catch (Exception ex) {
+ }
+ Pen pen = new Pen(color);
+ try {
+ String[] penTokens = tokens[tokens.length - 1].split(":");
+ color = (Color) Color.class.getField(penTokens[0].toUpperCase()).get(null);
+ int width = Integer.valueOf(penTokens[1]);
+ Pen.LineStyle style = Pen.LineStyle.valueOf(penTokens[2]);
+ pen = new Pen(color, width, style);
+ } catch (Exception ex) {
+ }
+ switch (type) {
+ case "Point":
+ uo.obj = new Overlays.Crosshairs();
+ uo.obj.setSize(new Dimension(Integer.valueOf(tokens[2]), Integer.valueOf(tokens[3])));
+ break;
+ case "Line":
+ uo.obj = new Overlays.Line();
+ break;
+ case "Arrow":
+ uo.obj = new Overlays.Arrow();
+ break;
+ case "Rect":
+ uo.obj = new Overlays.Rect();
+ break;
+ case "Ellipse":
+ uo.obj = new Overlays.Ellipse();
+ break;
+ case "Polyline":
+ uo.obj = new Overlays.Polyline();
+ break;
+ }
+ if (type.equals("Polyline") || type.equals("Point")) {
+ uo.channels = new String[]{tokens[0], tokens[1]};
+ } else {
+ uo.channels = new String[]{tokens[0], tokens[1], tokens[2], tokens[3]};
+ }
+ uo.obj.setPen(pen);
+ userOverlayConfig.add(uo);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ Overlay[] getUserOverlays(Data data) {
+ ArrayList ret = new ArrayList<>();
+ if (server != null) {
+ for (UserOverlay uo : userOverlayConfig) {
+ try {
+ Overlay ov = uo.obj;
+ //Overlay ov = (Overlay)uo.cls.newInstance();
+ ov.setCalibration(renderer.getCalibration());
+ boolean valid = false;
+ if (ov instanceof Overlays.Polyline) {
+ double[] x = (uo.channels[0].equals("null")) ? null : getServerDoubleArray(uo.channels[0]);
+ double[] y = (uo.channels[1].equals("null")) ? null : getServerDoubleArray(uo.channels[1]);
+ if ((x != null) || (y != null)) {
+ if (x == null) {
+ x = (renderer.getCalibration() == null) ? Arr.indexesDouble(y.length) : renderer.getCalibration().getAxisX(y.length);
+ }
+ if (y == null) {
+ y = (renderer.getCalibration() == null) ? Arr.indexesDouble(x.length) : renderer.getCalibration().getAxisY(x.length);
+ }
+ ((Overlays.Polyline) ov).updateAbsolute(x, y);
+ valid = true;
+ }
+ } else {
+ Double x = getServerDouble(uo.channels[0]);
+ Double y = getServerDouble(uo.channels[1]);
+ if ((x != null) && (y != null)) {
+ PointDouble position = new PointDouble(x, y);
+ ov.setAbsolutePosition(position);
+ if (!(ov instanceof Overlays.Crosshairs)) {
+ Double x2 = getServerDouble(uo.channels[2]);
+ Double y2 = getServerDouble(uo.channels[3]);
+ if ((x != null) && (y != null)) {
+ DimensionDouble size = new DimensionDouble(x2 - position.x, y2 - position.y);
+ ov.setAbsoluteSize(size);
+ valid = true;
+ }
+ } else {
+ valid = true;
+ }
+ }
+ }
+ if (valid) {
+ ret.add(ov);
+ }
+ } catch (Exception ex) {
+ //ex.printStackTrace();
+ }
+ }
+ }
+ return ret.toArray(new Overlay[0]);
+ }
+
+ double[] getComRms(double[] arr, double[] x) {
+ if (arr != null) {
+ double xmd = 0;
+ double xmd2 = 0;
+ double total = 0;
+ for (int i = 0; i < arr.length; i++) {
+ double v = (arr[i] * x[i]);
+ xmd += v;
+ xmd2 += (v * x[i]);
+ total += arr[i];
+ }
+ if (total > 0) {
+ double com = xmd / total;
+ double com2 = xmd2 / total;
+ double rms = Math.sqrt(Math.abs(com2 - com * com));
+ return new double[]{com, rms};
+ }
+ }
+ return new double[]{Double.NaN, Double.NaN};
+ }
+
+ double[] fitGaussianScript(int[] y, int[] x) {
+ ScriptManager sm = Context.getInstance().getScriptManager();
+ ArrayProperties pY = ArrayProperties.get(y);
+ sm.setVar("y", y);
+ sm.setVar("x", x);
+ InterpreterResult r = sm.eval("r = fit_gaussians(y, x, [" + pY.maxIndex + ",])");
+ if (r.exception != null) {
+ r.exception.printStackTrace();
+ } else {
+ List ret = (List) sm.getVar("r");
+ if ((ret != null) && (ret.size() == 1) && (ret.get(0) instanceof List) && (((List) (ret.get(0))).size() == 3)) {
+ double norm = (Double) ((List) ret.get(0)).get(0);
+ double mean = (Double) ((List) ret.get(0)).get(1);
+ double sigma = (Double) ((List) ret.get(0)).get(2);
+ return new double[]{norm, mean, sigma};
+ }
+ }
+ return null;
+ }
+
+ double[] fitGaussian(double[] y, int[] x) {
+ try {
+ ArrayProperties pY = ArrayProperties.get(y);
+ GaussianCurveFitter fitter = GaussianCurveFitter.create().withStartPoint(new double[]{(pY.max - pY.min) / 2, x[pY.maxIndex], 1.0}).withMaxIterations(1000);
+ ArrayList values = new ArrayList<>();
+ for (int i = 0; i < y.length; i++) {
+ values.add(new WeightedObservedPoint(1.0, x[i], y[i]));
+ }
+ return fitter.fit(values);
+ } catch (Exception ex) {
+ return null;
+ }
+
+ }
+
+ double[] fitPolynomial(PointDouble[] points, int order) {
+ double[] y = new double[points.length];
+ double[] x = new double[points.length];
+ for (int i = 0; i < points.length; i++) {
+ x[i] = points[i].x;
+ y[i] = points[i].y;
+ }
+ return fitPolynomial(y, x, order);
+ }
+
+ double[] fitPolynomial(double[] y, double[] x, int order) {
+ try {
+ ArrayProperties pY = ArrayProperties.get(y);
+ PolynomialCurveFitter fitter = PolynomialCurveFitter.create(order).withMaxIterations(1000);
+ ArrayList values = new ArrayList<>();
+ for (int i = 0; i < y.length; i++) {
+ values.add(new WeightedObservedPoint(1.0, x[i], y[i]));
+ }
+ return fitter.fit(values);
+ } catch (Exception ex) {
+ return null;
+ }
+
+ }
+
+ double[] getFitFunction(double[] pars, int[] x) {
+ double[] fit = new double[x.length];
+ Gaussian g = new Gaussian(pars[0], pars[1], pars[2]);
+ for (int i = 0; i < x.length; i++) {
+ fit[i] = g.value(x[i]);
+ }
+ return fit;
+ }
+
+ void setHistogramVisible(boolean value) {
+ if (value) {
+ if ((histogramDialog == null) || (!histogramDialog.isShowing())) {
+ Histogram histogram = new Histogram(true);
+ histogram.setRenderer(renderer);
+ histogramDialog = SwingUtils.showDialog(SwingUtils.getWindow(renderer), "Histogram", null, histogram);
+ renderer.refresh();
+ }
+ } else {
+ if (histogramDialog != null) {
+ histogramDialog.setVisible(false);
+ histogramDialog = null;
+ }
+ }
+ }
+
+ void setLaserState(boolean value) throws Exception {
+ System.out.println("Setting laser state: " + value);
+ Epics.putq("SIN-TIMAST-TMA:Beam-Las-Delay-Sel", value ? 0 : 1);
+ Epics.putq("SIN-TIMAST-TMA:Beam-Apply-Cmd.PROC", 1);
+ Thread.sleep(3000);
+ }
+
+ boolean getLaserState() throws Exception {
+ return (Epics.get("SIN-TIMAST-TMA:Beam-Las-Delay-Sel", Integer.class) == 0);
+ }
+
+ void elog(String logbook, String title, String message, String[] attachments) throws Exception {
+ String domain = "";
+ String category = "Info";
+ String entry = "";
+ StringBuffer cmd = new StringBuffer();
+
+ cmd.append("G_CS_ELOG_add -l \"").append(logbook).append("\" ");
+ cmd.append("-a \"Author=ScreenPanel\" ");
+ cmd.append("-a \"Type=pshell\" ");
+ cmd.append("-a \"Entry=").append(entry).append("\" ");
+ cmd.append("-a \"Title=").append(title).append("\" ");
+ cmd.append("-a \"Category=").append(category).append("\" ");
+ cmd.append("-a \"Domain=").append(domain).append("\" ");
+ for (String attachment : attachments) {
+ cmd.append("-f \"").append(attachment).append("\" ");
+ }
+ cmd.append("-n 1 ");
+ cmd.append("\"").append(message).append("\" ");
+ System.out.println(cmd.toString());
+
+ final Process process = Runtime.getRuntime().exec(new String[]{"bash", "-c", cmd.toString()});
+ new Thread(() -> {
+ try {
+ process.waitFor();
+ int bytes = process.getInputStream().available();
+ byte[] arr = new byte[bytes];
+ process.getInputStream().read(arr, 0, bytes);
+ System.out.println(new String(arr));
+ bytes = process.getErrorStream().available();
+ arr = new byte[bytes];
+ process.getErrorStream().read(arr, 0, bytes);
+ System.err.println(new String(arr));
+ } catch (Exception ex) {
+ System.err.println(ex);
+ }
+ }).start();
+ }
+
+ void centralizeRenderer() {
+ Point center = null;
+ Dimension size = renderer.getImageSize();
+ double zoom = (renderer.getMode() == RendererMode.Fixed) ? 1.0 : renderer.getZoom();
+ if (renderer.getCalibration() != null) {
+ center = renderer.getCalibration().getCenter();
+ } else if (size != null) {
+ center = new Point(size.width / 2, size.height / 2);
+ }
+ if (center != null) {
+ Point topleft = new Point(Math.max((int) (center.x - renderer.getWidth() / 2 / zoom), 0),
+ Math.max((int) (center.y - renderer.getHeight() / 2 / zoom), 0));
+ renderer.setViewPosition(topleft);
+ }
+ }
+
+ void updatePause() {
+ int index = ((int) pauseSelection.getValue()) - 1;
+ synchronized (imageBuffer) {
+ if (index < imageBuffer.size()) {
+ Data data = imageBuffer.get(index).data;
+ long pid = imageBuffer.get(index).cache.getPulseId();
+ BufferedImage image = camera.generateImage(data);
+ renderer.setImage(renderer.getOrigin(), image, data);
+
+ String text = "PID: " + pid;
+ if (imagePauseOverlay == null) {
+ Font font = new Font("Verdana", Font.PLAIN, 12);
+ Dimension d = SwingUtils.getTextSize(text, renderer.getFontMetrics(font));
+ imagePauseOverlay = new Text(renderer.getPenErrorText(), "", font, new Point(-20 - d.width, 42));
+ imagePauseOverlay.setFixed(true);
+ imagePauseOverlay.setAnchor(Overlay.ANCHOR_VIEWPORT_OR_IMAGE_TOP_RIGHT);
+ renderer.addOverlay(imagePauseOverlay);
+ }
+ //imagePauseOverlay.update(Chrono.getTimeStr(data.getTimestamp(), "HH:mm:ss.SSS"));
+ imagePauseOverlay.update(text);
+ manageFit(image, data);
+ manageUserOverlays(image, data);
+ }
+ }
+ updateStreamData();
+ }
+
+ void removePauseOverlay() {
+ renderer.removeOverlay(imagePauseOverlay);
+ imagePauseOverlay = null;
+ }
+
+ void saveSnapshot() throws Exception {
+ String snapshotFile = null;
+ synchronized (imageBuffer) {
+ Frame frame = getCurrentFrame();
+ if (frame == null) {
+ throw new Exception("No current image");
+ }
+ ArrayList frames = new ArrayList<>();
+ frames.add(frame);
+ this.saveFrames(cameraName + "_camera_snapshot", frames);
+
+ //Enforce the same timestamp to data & image files.
+ snapshotFile = getContext().getExecutionPars().getPath() + ".png";
+ ImageBuffer.saveImage(SwingUtils.createImage(renderer), snapshotFile, "png");
+ }
+
+ JPanel panel = new JPanel();
+ GridBagLayout layout = new GridBagLayout();
+ layout.columnWidths = new int[]{0, 180}; //Minimum width
+ layout.rowHeights = new int[]{30, 30, 30}; //Minimum height
+ panel.setLayout(layout);
+ JComboBox comboLogbook = new JComboBox(new String[]{"SwissFEL commissioning data", "SwissFEL commissioning"});
+ JTextField textComment = new JTextField();
+ GridBagConstraints c = new GridBagConstraints();
+ c.gridx = 0;
+ c.gridy = 0;
+ panel.add(new JLabel("Data file:"), c);
+ c.gridy = 1;
+ panel.add(new JLabel("Logbook:"), c);
+ c.gridy = 2;
+ panel.add(new JLabel("Comment:"), c);
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridx = 1;
+ panel.add(textComment, c);
+ c.gridy = 1;
+ panel.add(comboLogbook, c);
+ c.gridy = 0;
+ panel.add(new JLabel(getContext().getExecutionPars().getPath()), c);
+
+ if (SwingUtils.showOption(getTopLevel(), "Success", panel, OptionType.OkCancel) == OptionResult.Yes) {
+ StringBuilder message = new StringBuilder();
+ message.append("Camera: ").append(cameraName).append(" (").
+ append((server != null) ? "server" : "direct").append(")").append("\n");
+ message.append("Screen: ").append(String.valueOf(valueScreen.getLabel().getText())).append("\n");
+ message.append("Filter: ").append(String.valueOf(valueFilter.getLabel().getText())).append("\n");
+ message.append("Data file: ").append(getContext().getExecutionPars().getPath()).append("\n");
+ message.append("Comment: ").append(textComment.getText()).append("\n");
+ //Add slicing message
+ if ((fitOv != null) && (fitOv.length > 5) && (fitOv[fitOv.length-1] instanceof Overlays.Text)) {
+ Overlays.Text text = (Overlays.Text) fitOv[fitOv.length-1];
+ message.append(text.getText()).append("\n");
+ }
+ elog((String) comboLogbook.getSelectedItem(), "ScreenPanel Snapshot", message.toString(), new String[]{snapshotFile});
+ }
+ }
+
+ void saveStack() throws Exception {
+ synchronized (imageBuffer) {
+ saveFrames(cameraName + "_camera_stack", imageBuffer);
+ }
+ SwingUtils.showMessage(getTopLevel(), "Success", "Generated data file:\n" + getContext().getExecutionPars().getPath());
+ }
+
+ public static String getCameraType(String name) {
+ if (name == null) {
+ return "";
+ }
+ for (String s : new String[]{"LCAM"}) {
+ if (name.contains(s)) {
+ return LASER_TYPE;
+ }
+ }
+ for (String s : new String[]{"DSCR", "DSRM", "DLAC"}) {
+ if (name.contains(s)) {
+ return ELECTRONS_TYPE;
+ }
+ }
+ for (String s : new String[]{"PROF", "PPRM", "PSSS", "PSCR", "PSRD"}) {
+ if (name.contains(s)) {
+ return PHOTONICS_TYPE;
+ }
+ }
+ return "Unknown";
+ }
+
+ public Map getProcessingParameters(StreamValue value) throws IOException {
+ return (Map) JsonSerializer.decode(value.getValue("processing_parameters").toString(), Map.class);
+ }
+
+ void saveFrames(String name, ArrayList frames) throws IOException {
+ ArrayList values = new ArrayList<>();
+ for (Frame frame : frames) {
+ values.add(frame.cache);
+ }
+ saveImages(name, values);
+ }
+
+ void saveImages(String name, ArrayList images) throws IOException {
+ int depth = images.size();
+ if (depth == 0) {
+ return;
+ }
+ StreamValue first = images.get(0);
+ String pathRoot = "/camera1/";
+ String pathImage = pathRoot + "image";
+ String pathPid = pathRoot + "pulse_id";
+ String pathTimestampStr = pathRoot + "timestamp_str";
+ Map processingPars = getProcessingParameters(first);
+ String camera = (String) processingPars.get("camera_name");
+ String type = getCameraType(camera);
+
+ int width = ((Number) first.getValue("width")).intValue();
+ int height = ((Number) first.getValue("height")).intValue();
+ Class dataType = first.getValue("image").getClass().getComponentType();
+
+ getContext().setExecutionPars(name);
+ DataManager dm = getContext().getDataManager();
+
+ //Create tables
+ dm.createDataset(pathImage, dataType, new int[]{depth, height, width});
+ dm.createDataset(pathPid, Long.class, new int[]{depth});
+ dm.createDataset(pathTimestampStr, String.class, new int[]{depth});
+ for (String id : first.getIdentifiers()) {
+ Object val = first.getValue(id);
+ if (id.equals("image")) {
+ } else if (id.equals("processing_parameters")) {
+ Map pars = getProcessingParameters(first);
+ for (String key : pars.keySet()) {
+ if ((pars.get(key) != null) && (pars.get(key) instanceof Map)) {
+ for (Object k : ((Map) pars.get(key)).keySet()) {
+ Object v = ((Map) pars.get(key)).get(k);
+ dm.setAttribute(pathImage, key + " " + k, (v == null) ? "" : v);
+ }
+ } else {
+ Object value = pars.get(key);
+ if (value == null) {
+ value = "";
+ } else if (value instanceof List) {
+ Class cls = (((List) value).size() > 0) ? ((List) value).get(0).getClass() : double.class;
+ value = Convert.toPrimitiveArray(value, cls);
+ //value = Convert.toDouble(value);
+ }
+ dm.setAttribute(pathImage, key, value);
+ }
+ }
+ } else if (val.getClass().isArray()) {
+ dm.createDataset(pathRoot + id, Double.class, new int[]{depth, Array.getLength(val)});
+ } else {
+ dm.createDataset(pathRoot + id, val.getClass(), new int[]{depth});
+ }
+ }
+
+ //Add metadata
+ dm.setAttribute(pathRoot, "Camera", camera);
+ dm.setAttribute(pathRoot, "Images", depth);
+ dm.setAttribute(pathRoot, "Interval", -1);
+ dm.setAttribute(pathRoot, "Type", type);
+ if (type.equals(ELECTRONS_TYPE)) {
+ dm.setAttribute(pathRoot, "Screen", String.valueOf(valueScreen.getLabel().getText()));
+ dm.setAttribute(pathRoot, "Filter", String.valueOf(valueFilter.getLabel().getText()));
+ }
+
+ //Save data
+ for (int index = 0; index < depth; index++) {
+ StreamValue streamValue = images.get(index);
+ dm.setItem(pathImage, streamValue.getValue("image"), new long[]{index, 0, 0}, new int[]{1, height, width});
+ dm.setItem(pathPid, streamValue.getPulseId(), index);
+ dm.setItem(pathTimestampStr, Chrono.getTimeStr(streamValue.getTimestamp(), "YYYY-MM-dd HH:mm:ss.SSS"), index);
+
+ for (String id : streamValue.getIdentifiers()) {
+ Object val = streamValue.getValue(id);
+ if (id.equals("image")) {
+ } else if (id.equals("processing_parameters")) {
+ } else if (val.getClass().isArray()) {
+ dm.setItem(pathRoot + id, val, index);
+ } else {
+ dm.setItem(pathRoot + id, val, index);
+ }
+ }
+ }
+ getContext().getDataManager().closeOutput();
+ }
+
+ StandardDialog calibrationDialolg;
+
+ void calibrate() throws Exception {
+ if (server != null) {
+ server.resetRoi();
+ calibrationDialolg = (StandardDialog) getContext().getClassByName("CameraCalibrationDialog").getConstructors()[0].newInstance(new Object[]{getTopLevel(), server.getCurrentCamera(), renderer});
+ SwingUtils.centerComponent(getTopLevel(), calibrationDialolg);
+ calibrationDialolg.setVisible(true);
+ calibrationDialolg.setListener(new StandardDialogListener() {
+ @Override
+ public void onWindowOpened(StandardDialog dlg) {
+ }
+
+ @Override
+ public void onWindowClosed(StandardDialog dlg, boolean accepted) {
+ if (accepted) {
+ //comboCamerasActionPerformed(null);
+ }
+ }
+ });
+ }
+ }
+
+ StandardDialog dataTableDialog;
+ DefaultTableModel dataTableModel;
+ JTable dataTable;
+
+ void showStreamData() {
+ dataTableModel = null;
+ if (server != null) {
+
+ if ((dataTableDialog != null) && (dataTableDialog.isShowing())) {
+ SwingUtils.centerComponent(getTopLevel(), dataTableDialog);
+ dataTableDialog.requestFocus();
+ return;
+ }
+ dataTableModel = new DefaultTableModel(new Object[0][2], new String[]{"Name", "Value"}) {
+ public Class getColumnClass(int columnIndex) {
+ return String.class;
+ }
+
+ public boolean isCellEditable(int rowIndex, int columnIndex) {
+ return false;
+ }
+ };
+ updateStreamData();
+ StreamValue val = server.getStream().take();
+ dataTable = new JTable(dataTableModel);
+ dataTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
+ dataTable.setCellSelectionEnabled(true);
+ dataTable.getTableHeader().setReorderingAllowed(false);
+ dataTable.getTableHeader().setResizingAllowed(true);
+ dataTableDialog = new StandardDialog(getTopLevel(), "Image Data", false);
+ dataTableDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+ JScrollPane scrollPane = new JScrollPane();
+ scrollPane.setViewportView(dataTable);
+ scrollPane.setPreferredSize(new Dimension(300, 400));
+ dataTableDialog.setContentPane(scrollPane);
+ dataTableDialog.pack();
+ dataTableDialog.setVisible(true);
+ dataTableDialog.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosing(WindowEvent e) {
+ dataTableModel = null;
+ }
+ });
+ dataTable.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ try {
+ int row = dataTable.getSelectedRow();
+ int col = dataTable.getSelectedColumn();
+ dataTable.setToolTipText(null);
+ if (row > 1) {
+ String id = String.valueOf(dataTable.getModel().getValueAt(row, 0));
+ String locator = String.valueOf(dataTable.getModel().getValueAt(0, 1));
+ String channelId = locator + " " + id;
+ dataTable.setToolTipText(channelId);
+ if ((e.getClickCount() == 2) && (!e.isPopupTrigger())) {
+ if (col == 0) {
+ SwingUtils.showMessage(dataTableDialog, "Channel Identifier", "Copied to clipboard: " + channelId);
+ Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+ clipboard.setContents(new StringSelection(channelId), (Clipboard clipboard1, Transferable contents) -> {
+ });
+ } else {
+ Object obj = getCurrentFrame().cache.getValue(id);
+ if (id.equals("image")) {
+ } else if (id.equals("processing_parameters")) {
+ Map pars = getProcessingParameters(getCurrentFrame().cache);
+ StringBuilder sb = new StringBuilder();
+ for (String key : pars.keySet()) {
+ sb.append(key).append(" = ").append(Str.toString(pars.get(key), 10)).append("\n");
+ }
+ SwingUtils.showMessage(dataTableDialog, "Processing Parameters", sb.toString());
+ } else if ((obj != null) && (obj.getClass().isArray() || (obj instanceof Number))) {
+ DeviceValueChart chart = new DeviceValueChart();
+ Device dev = null;
+ if (obj.getClass().isArray()) {
+ dev = new ReadableRegisterArray(new ReadableArray() {
+ @Override
+ public Object read() throws IOException, InterruptedException {
+ return Convert.toDouble(getCurrentFrame().cache.getValue(id));
+ }
+
+ @Override
+ public int getSize() {
+ return Array.getLength(getCurrentFrame().cache.getValue(id));
+ }
+ });
+ } else {
+ dev = new ReadableRegisterNumber(new ReadableNumber() {
+ @Override
+ public Object read() throws IOException, InterruptedException {
+ return Convert.toDouble(getCurrentFrame().cache.getValue(id));
+ }
+ });
+ }
+ dev.setPolling(1000);
+ chart.setDevice(dev);
+ JDialog dlg = SwingUtils.showDialog(dataTableDialog, cameraName + " " + id, null, chart);
+ //TODO:
+ //PlotBase plot = chart.getPlot();
+ //if (plot!=null){
+ // plot.setPlotBackgroundColor(Color.BLACK);
+ //}
+ }
+ }
+ }
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }
+ });
+ SwingUtils.centerComponent(getTopLevel(), dataTableDialog);
+ updateStreamData();
+ }
+ }
+
+ volatile boolean updatingStreamData = false;
+
+ void updateStreamData() {
+ if ((dataTableDialog == null) || !dataTableDialog.isShowing() || updatingStreamData) {
+ return;
+ }
+ updatingStreamData = true;
+ SwingUtilities.invokeLater(() -> {
+ updatingStreamData = false;
+ if ((dataTableModel != null) && (server != null)) {
+ StreamValue value = server.getValue();
+ Frame frame = getCurrentFrame();
+ int[] sel_rows = (dataTable == null) ? null : dataTable.getSelectedRows();
+ int[] sel_cols = (dataTable == null) ? null : dataTable.getSelectedColumns();
+ List ids = (value == null) ? new ArrayList<>() : new ArrayList(value.getIdentifiers());
+ if (ids.size() + 4 != dataTableModel.getRowCount()) {
+ dataTableModel.setNumRows(0);
+ try {
+ dataTableModel.addRow(new Object[]{"Locator", server.getUrl() + "/" + ((value == null) ? instanceName : server.getCurrentInstance())});
+ } catch (Exception ex) {
+ dataTableModel.addRow(new Object[]{"Locator", ex.getMessage()});
+ }
+ try {
+ dataTableModel.addRow(new Object[]{"Stream", server.getStreamAddress()});
+ } catch (Exception ex) {
+ dataTableModel.addRow(new Object[]{"Stream", ex.getMessage()});
+ }
+ dataTableModel.addRow(new Object[]{"PID", ""});
+ dataTableModel.addRow(new Object[]{"Timestamp", ""});
+ Collections.sort(ids);
+ for (String id : ids) {
+ dataTableModel.addRow(new Object[]{id, ""});
+ }
+ }
+
+ if ((frame != null) && (frame.cache != null)) {
+ dataTableModel.setValueAt(frame.cache.getPulseId(), 2, 1); //PID
+ dataTableModel.setValueAt(frame.cache.getTimestamp(), 3, 1); //Timestamp
+ for (int i = 4; i < dataTableModel.getRowCount(); i++) {
+ String id = String.valueOf(dataTableModel.getValueAt(i, 0));
+ //Object obj = server.getValue(id);
+ Object obj = frame.cache.getValue(id);
+ if (obj != null) {
+ if (obj.getClass().isArray()) {
+ obj = obj.getClass().getComponentType().getSimpleName() + "[" + Array.getLength(obj) + "]";
+ } else if (obj instanceof Double) {
+ obj = Convert.roundDouble((Double) obj, 1);
+ } else if (obj instanceof Float) {
+ obj = Convert.roundDouble((Float) obj, 1);
+ }
+ }
+ dataTableModel.setValueAt(String.valueOf(obj), i, 1);
+ }
+ }
+ if ((sel_rows != null) && (sel_rows.length > 0)) {
+ //dataTable.setRowSelectionInterval((Integer)Arr.getMin(sel_rows), (Integer)Arr.getMax(sel_rows));
+ dataTable.setRowSelectionInterval(sel_rows[0], sel_rows[sel_rows.length - 1]);
+ }
+ if ((sel_cols != null) && (sel_cols.length > 0)) {
+ //dataTable.setColumnSelectionInterval((Integer)Arr.getMin(sel_cols), (Integer)Arr.getMax(sel_cols));
+ dataTable.setColumnSelectionInterval(sel_cols[0], sel_cols[sel_cols.length - 1]);
+ }
+ }
+ });
+ }
+
+ ImageIcon getIcon(String name) {
+ ImageIcon ret = null;
+ try {
+ //Path path = Paths.get(getClass().getProtectionDomain().getCodeSource().getLocation().getPath(),"resources", name + ".png");
+ String dir = getClass().getProtectionDomain().getCodeSource().getLocation().getPath() + "resources/";
+ if (new File(dir + name + ".png").exists()) {
+ ret = new javax.swing.ImageIcon(dir + name + ".png");
+ } else {
+ ret = new ImageIcon(ch.psi.pshell.ui.App.class.getResource("/ch/psi/pshell/ui/" + name + ".png"));
+ if (MainFrame.isDark()) {
+ try {
+ ret = new ImageIcon(ch.psi.pshell.ui.App.class.getResource("/ch/psi/pshell/ui/dark/" + name + ".png"));
+ } catch (Exception e) {
+ }
+ }
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ return ret;
+ }
+
+ String getIconName(JButton button) {
+ String ret = button.getIcon().toString();
+ if (ret.indexOf(".") > 0) {
+ ret = ret.substring(0, ret.indexOf("."));
+ }
+ return ret;
+ }
+
+ ////////
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ buttonGroup1 = new javax.swing.ButtonGroup();
+ buttonGroup2 = new javax.swing.ButtonGroup();
+ buttonGroup3 = new javax.swing.ButtonGroup();
+ buttonGroup4 = new javax.swing.ButtonGroup();
+ jProgressBar1 = new javax.swing.JProgressBar();
+ sidePanel = new javax.swing.JPanel();
+ jPanel3 = new javax.swing.JPanel();
+ buttonZoomFit = new javax.swing.JRadioButton();
+ buttonZoomStretch = new javax.swing.JRadioButton();
+ buttonZoomNormal = new javax.swing.JRadioButton();
+ buttonZoom025 = new javax.swing.JRadioButton();
+ buttonZoom05 = new javax.swing.JRadioButton();
+ buttonZoom2 = new javax.swing.JRadioButton();
+ jPanel2 = new javax.swing.JPanel();
+ checkHistogram = new javax.swing.JCheckBox();
+ comboColormap = new javax.swing.JComboBox();
+ jLabel3 = new javax.swing.JLabel();
+ jLabel4 = new javax.swing.JLabel();
+ buttonFullRange = new javax.swing.JRadioButton();
+ buttonManual = new javax.swing.JRadioButton();
+ buttonAutomatic = new javax.swing.JRadioButton();
+ labelMin = new javax.swing.JLabel();
+ spinnerMin = new javax.swing.JSpinner();
+ spinnerMax = new javax.swing.JSpinner();
+ labelMax = new javax.swing.JLabel();
+ btFixColormapRange = new javax.swing.JButton();
+ jPanel5 = new javax.swing.JPanel();
+ buttonServer = new javax.swing.JRadioButton();
+ buttonDirect = new javax.swing.JRadioButton();
+ textState = new javax.swing.JTextField();
+ filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
+ panelScreen = new javax.swing.JPanel();
+ valueScreen = new ch.psi.pshell.swing.DeviceValuePanel();
+ comboScreen = new javax.swing.JComboBox();
+ panelFilter = new javax.swing.JPanel();
+ valueFilter = new ch.psi.pshell.swing.DeviceValuePanel();
+ comboFilter = new javax.swing.JComboBox();
+ panelScreen2 = new javax.swing.JPanel();
+ checkThreshold = new javax.swing.JCheckBox();
+ spinnerThreshold = new javax.swing.JSpinner();
+ checkBackground = new javax.swing.JCheckBox();
+ checkGoodRegion = new javax.swing.JCheckBox();
+ spinnerGrScale = new javax.swing.JSpinner();
+ spinnerGrThreshold = new javax.swing.JSpinner();
+ labelGrThreshold = new javax.swing.JLabel();
+ labelGrScale = new javax.swing.JLabel();
+ panelSlicing = new javax.swing.JPanel();
+ checkSlicing = new javax.swing.JCheckBox();
+ labelSlScale = new javax.swing.JLabel();
+ spinnerSlScale = new javax.swing.JSpinner();
+ labelSlNumber = new javax.swing.JLabel();
+ spinnerSlNumber = new javax.swing.JSpinner();
+ labelSlOrientation = new javax.swing.JLabel();
+ spinnerSlOrientation = new javax.swing.JSpinner();
+ topPanel = new javax.swing.JPanel();
+ toolBar = new javax.swing.JToolBar();
+ buttonSidePanel = new javax.swing.JToggleButton();
+ buttonStreamData = new javax.swing.JButton();
+ buttonSave = new javax.swing.JToggleButton();
+ buttonGrabBackground = new javax.swing.JButton();
+ buttonPause = new javax.swing.JToggleButton();
+ jSeparator6 = new javax.swing.JToolBar.Separator();
+ buttonMarker = new javax.swing.JToggleButton();
+ buttonProfile = new javax.swing.JToggleButton();
+ buttonFit = new javax.swing.JToggleButton();
+ buttonReticle = new javax.swing.JToggleButton();
+ buttonTitle = new javax.swing.JToggleButton();
+ pauseSelection = new ch.psi.pshell.swing.ValueSelection();
+ panelCameraSelection = new javax.swing.JPanel();
+ jLabel1 = new javax.swing.JLabel();
+ comboCameras = new javax.swing.JComboBox();
+ jLabel5 = new javax.swing.JLabel();
+ comboType = new javax.swing.JComboBox();
+ renderer = new ch.psi.pshell.imaging.Renderer();
+
+ setPreferredSize(new java.awt.Dimension(873, 600));
+
+ jPanel3.setBorder(javax.swing.BorderFactory.createTitledBorder("Zoom"));
+
+ buttonGroup1.add(buttonZoomFit);
+ buttonZoomFit.setText("Fit");
+ buttonZoomFit.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonZoomFitActionPerformed(evt);
+ }
+ });
+
+ buttonGroup1.add(buttonZoomStretch);
+ buttonZoomStretch.setText("Stretch");
+ buttonZoomStretch.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonZoomStretchActionPerformed(evt);
+ }
+ });
+
+ buttonGroup1.add(buttonZoomNormal);
+ buttonZoomNormal.setText("Normal");
+ buttonZoomNormal.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonZoomNormalActionPerformed(evt);
+ }
+ });
+
+ buttonGroup1.add(buttonZoom025);
+ buttonZoom025.setText("1/4");
+ buttonZoom025.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonZoom025ActionPerformed(evt);
+ }
+ });
+
+ buttonGroup1.add(buttonZoom05);
+ buttonZoom05.setText("1/2");
+ buttonZoom05.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonZoom05ActionPerformed(evt);
+ }
+ });
+
+ buttonGroup1.add(buttonZoom2);
+ buttonZoom2.setText("2");
+ buttonZoom2.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonZoom2ActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
+ jPanel3.setLayout(jPanel3Layout);
+ jPanel3Layout.setHorizontalGroup(
+ jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel3Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(buttonZoomFit)
+ .addComponent(buttonZoomNormal)
+ .addComponent(buttonZoomStretch))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(buttonZoom025)
+ .addComponent(buttonZoom05)
+ .addComponent(buttonZoom2))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+ jPanel3Layout.setVerticalGroup(
+ jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel3Layout.createSequentialGroup()
+ .addGap(4, 4, 4)
+ .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(buttonZoomNormal)
+ .addComponent(buttonZoom025))
+ .addGap(0, 0, 0)
+ .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(buttonZoomFit)
+ .addComponent(buttonZoom05))
+ .addGap(0, 0, 0)
+ .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(buttonZoomStretch)
+ .addComponent(buttonZoom2))
+ .addContainerGap())
+ );
+
+ jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder("Colormap"));
+
+ checkHistogram.setText("Histogram");
+ checkHistogram.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ checkHistogramActionPerformed(evt);
+ }
+ });
+
+ comboColormap.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ onChangeColormap(evt);
+ }
+ });
+
+ jLabel3.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
+ jLabel3.setText("Type:");
+
+ jLabel4.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
+ jLabel4.setText("Range:");
+
+ buttonGroup3.add(buttonFullRange);
+ buttonFullRange.setText("Full");
+ buttonFullRange.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ onChangeColormap(evt);
+ }
+ });
+
+ buttonGroup3.add(buttonManual);
+ buttonManual.setText("Manual");
+ buttonManual.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ onChangeColormap(evt);
+ }
+ });
+
+ buttonGroup3.add(buttonAutomatic);
+ buttonAutomatic.setText("Automatic");
+ buttonAutomatic.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ onChangeColormap(evt);
+ }
+ });
+
+ labelMin.setText("Min:");
+
+ spinnerMin.setModel(new javax.swing.SpinnerNumberModel(0, 0, 65535, 1));
+ spinnerMin.setEnabled(false);
+ spinnerMin.setPreferredSize(new java.awt.Dimension(77, 20));
+ spinnerMin.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ onChangeColormapRange(evt);
+ }
+ });
+
+ spinnerMax.setModel(new javax.swing.SpinnerNumberModel(255, 0, 65535, 1));
+ spinnerMax.setEnabled(false);
+ spinnerMax.setPreferredSize(new java.awt.Dimension(77, 20));
+ spinnerMax.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ onChangeColormapRange(evt);
+ }
+ });
+
+ labelMax.setText("Max:");
+
+ btFixColormapRange.setText("Fix");
+ btFixColormapRange.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btFixColormapRangeActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
+ jPanel2.setLayout(jPanel2Layout);
+ jPanel2Layout.setHorizontalGroup(
+ jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel2Layout.createSequentialGroup()
+ .addGap(4, 4, 4)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel3)
+ .addComponent(jLabel4))
+ .addGap(4, 4, 4)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(buttonAutomatic)
+ .addComponent(buttonFullRange)
+ .addComponent(buttonManual)
+ .addComponent(comboColormap, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
+ .addComponent(labelMax)
+ .addGap(2, 2, 2)
+ .addComponent(spinnerMax, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addComponent(checkHistogram, javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
+ .addComponent(labelMin)
+ .addGap(2, 2, 2)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(btFixColormapRange, javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(spinnerMin, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
+ .addContainerGap())
+ );
+
+ jPanel2Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {btFixColormapRange, spinnerMax, spinnerMin});
+
+ jPanel2Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {jLabel3, jLabel4});
+
+ jPanel2Layout.setVerticalGroup(
+ jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
+ .addGap(4, 4, 4)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(comboColormap, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel3)
+ .addComponent(checkHistogram))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(buttonAutomatic)
+ .addComponent(jLabel4)
+ .addComponent(btFixColormapRange, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGap(0, 0, 0)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(labelMin)
+ .addComponent(spinnerMin, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(buttonFullRange))
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(buttonManual)
+ .addComponent(labelMax)
+ .addComponent(spinnerMax, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addContainerGap())
+ );
+
+ jPanel5.setBorder(javax.swing.BorderFactory.createTitledBorder("Source"));
+
+ buttonGroup4.add(buttonServer);
+ buttonServer.setSelected(true);
+ buttonServer.setText("Server");
+ buttonServer.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonServerActionPerformed(evt);
+ }
+ });
+
+ buttonGroup4.add(buttonDirect);
+ buttonDirect.setText("Direct");
+ buttonDirect.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonDirectActionPerformed(evt);
+ }
+ });
+
+ textState.setEditable(false);
+ textState.setHorizontalAlignment(javax.swing.JTextField.CENTER);
+ textState.setDisabledTextColor(new java.awt.Color(0, 0, 0));
+ textState.setEnabled(false);
+
+ javax.swing.GroupLayout jPanel5Layout = new javax.swing.GroupLayout(jPanel5);
+ jPanel5.setLayout(jPanel5Layout);
+ jPanel5Layout.setHorizontalGroup(
+ jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel5Layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(buttonServer)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(buttonDirect)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(textState, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap())
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel5Layout.createSequentialGroup()
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(56, 56, 56))
+ );
+ jPanel5Layout.setVerticalGroup(
+ jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel5Layout.createSequentialGroup()
+ .addGap(4, 4, 4)
+ .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel5Layout.createSequentialGroup()
+ .addComponent(textState, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(buttonServer)
+ .addComponent(buttonDirect)))
+ .addContainerGap())
+ );
+
+ panelScreen.setBorder(javax.swing.BorderFactory.createTitledBorder("Screen"));
+
+ comboScreen.setEnabled(false);
+ comboScreen.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ comboScreenActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout panelScreenLayout = new javax.swing.GroupLayout(panelScreen);
+ panelScreen.setLayout(panelScreenLayout);
+ panelScreenLayout.setHorizontalGroup(
+ panelScreenLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelScreenLayout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(panelScreenLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(valueScreen, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(comboScreen, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addContainerGap())
+ );
+ panelScreenLayout.setVerticalGroup(
+ panelScreenLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelScreenLayout.createSequentialGroup()
+ .addGap(4, 4, 4)
+ .addComponent(comboScreen, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(valueScreen, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap())
+ );
+
+ panelFilter.setBorder(javax.swing.BorderFactory.createTitledBorder("Filter"));
+
+ comboFilter.setEnabled(false);
+ comboFilter.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ comboFilterActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout panelFilterLayout = new javax.swing.GroupLayout(panelFilter);
+ panelFilter.setLayout(panelFilterLayout);
+ panelFilterLayout.setHorizontalGroup(
+ panelFilterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelFilterLayout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(panelFilterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(valueFilter, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(comboFilter, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addContainerGap())
+ );
+ panelFilterLayout.setVerticalGroup(
+ panelFilterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelFilterLayout.createSequentialGroup()
+ .addGap(4, 4, 4)
+ .addComponent(comboFilter, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(valueFilter, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+
+ panelScreen2.setBorder(javax.swing.BorderFactory.createTitledBorder("Pipeline"));
+
+ checkThreshold.setText("Threshold");
+ checkThreshold.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ checkThresholdActionPerformed(evt);
+ }
+ });
+
+ spinnerThreshold.setModel(new javax.swing.SpinnerNumberModel(0.0d, 0.0d, 99999.0d, 1.0d));
+ spinnerThreshold.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ spinnerThresholdonChange(evt);
+ }
+ });
+
+ checkBackground.setText("Subtract Background");
+ checkBackground.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ checkBackgroundActionPerformed(evt);
+ }
+ });
+
+ checkGoodRegion.setText("Good Region");
+ checkGoodRegion.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ checkGoodRegionActionPerformed(evt);
+ }
+ });
+
+ spinnerGrScale.setModel(new javax.swing.SpinnerNumberModel(3.0d, 0.01d, 99999.0d, 1.0d));
+ spinnerGrScale.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ spinnerGrThresholdonChange(evt);
+ }
+ });
+
+ spinnerGrThreshold.setModel(new javax.swing.SpinnerNumberModel(0.5d, 0.04d, 1.0d, 0.1d));
+ spinnerGrThreshold.setPreferredSize(new java.awt.Dimension(92, 20));
+ spinnerGrThreshold.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ spinnerGrThresholdonChange(evt);
+ }
+ });
+
+ labelGrThreshold.setText("Threshold:");
+
+ labelGrScale.setText("Scale:");
+
+ checkSlicing.setText("Slicing");
+ checkSlicing.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ checkSlicingActionPerformed(evt);
+ }
+ });
+
+ labelSlScale.setText("Scale:");
+
+ spinnerSlScale.setModel(new javax.swing.SpinnerNumberModel(3.0d, 0.01d, 99999.0d, 1.0d));
+ spinnerSlScale.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ spinnerSlicingChange(evt);
+ }
+ });
+
+ labelSlNumber.setText("Slices:");
+
+ spinnerSlNumber.setModel(new javax.swing.SpinnerNumberModel(2, 0, 1000, 1));
+ spinnerSlNumber.setPreferredSize(new java.awt.Dimension(92, 20));
+ spinnerSlNumber.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ spinnerSlicingChange(evt);
+ }
+ });
+
+ labelSlOrientation.setText("Orientation:");
+
+ spinnerSlOrientation.setModel(new javax.swing.SpinnerListModel(new String[] {"vertical", "horizontal"}));
+ spinnerSlOrientation.setPreferredSize(new java.awt.Dimension(92, 20));
+ spinnerSlOrientation.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ spinnerSlicingChange(evt);
+ }
+ });
+
+ javax.swing.GroupLayout panelSlicingLayout = new javax.swing.GroupLayout(panelSlicing);
+ panelSlicing.setLayout(panelSlicingLayout);
+ panelSlicingLayout.setHorizontalGroup(
+ panelSlicingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelSlicingLayout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(panelSlicingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelSlicingLayout.createSequentialGroup()
+ .addComponent(checkSlicing)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(panelSlicingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelSlicingLayout.createSequentialGroup()
+ .addComponent(labelSlNumber)
+ .addGap(2, 2, 2)
+ .addComponent(spinnerSlNumber, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelSlicingLayout.createSequentialGroup()
+ .addComponent(labelSlScale)
+ .addGap(2, 2, 2)
+ .addComponent(spinnerSlScale, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelSlicingLayout.createSequentialGroup()
+ .addGap(0, 0, Short.MAX_VALUE)
+ .addComponent(labelSlOrientation)
+ .addGap(2, 2, 2)
+ .addComponent(spinnerSlOrientation, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ .addContainerGap())
+ );
+
+ panelSlicingLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {spinnerSlNumber, spinnerSlOrientation, spinnerSlScale});
+
+ panelSlicingLayout.setVerticalGroup(
+ panelSlicingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelSlicingLayout.createSequentialGroup()
+ .addGroup(panelSlicingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(checkSlicing)
+ .addGroup(panelSlicingLayout.createSequentialGroup()
+ .addGroup(panelSlicingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(spinnerSlNumber, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(labelSlNumber))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(panelSlicingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(spinnerSlScale, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(labelSlScale))))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(panelSlicingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(spinnerSlOrientation, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(labelSlOrientation)))
+ );
+
+ javax.swing.GroupLayout panelScreen2Layout = new javax.swing.GroupLayout(panelScreen2);
+ panelScreen2.setLayout(panelScreen2Layout);
+ panelScreen2Layout.setHorizontalGroup(
+ panelScreen2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelScreen2Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(panelScreen2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelScreen2Layout.createSequentialGroup()
+ .addComponent(checkThreshold)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelScreen2Layout.createSequentialGroup()
+ .addGroup(panelScreen2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelScreen2Layout.createSequentialGroup()
+ .addComponent(checkGoodRegion)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(labelGrScale))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelScreen2Layout.createSequentialGroup()
+ .addGap(0, 0, Short.MAX_VALUE)
+ .addComponent(labelGrThreshold)))
+ .addGap(2, 2, 2)))
+ .addGroup(panelScreen2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(spinnerGrThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(spinnerGrScale, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(spinnerThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addContainerGap())
+ .addComponent(panelSlicing, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(panelScreen2Layout.createSequentialGroup()
+ .addGap(6, 6, 6)
+ .addComponent(checkBackground)
+ .addGap(106, 106, 106))
+ );
+
+ panelScreen2Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {spinnerGrScale, spinnerGrThreshold, spinnerThreshold});
+
+ panelScreen2Layout.setVerticalGroup(
+ panelScreen2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelScreen2Layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(checkBackground)
+ .addGap(2, 2, 2)
+ .addGroup(panelScreen2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(checkThreshold)
+ .addComponent(spinnerThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGap(2, 2, 2)
+ .addGroup(panelScreen2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(checkGoodRegion)
+ .addComponent(spinnerGrScale, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(labelGrScale))
+ .addGap(2, 2, 2)
+ .addGroup(panelScreen2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(spinnerGrThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(labelGrThreshold))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(panelSlicing, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap())
+ );
+
+ javax.swing.GroupLayout sidePanelLayout = new javax.swing.GroupLayout(sidePanel);
+ sidePanel.setLayout(sidePanelLayout);
+ sidePanelLayout.setHorizontalGroup(
+ sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(sidePanelLayout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
+ .addComponent(jPanel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jPanel3, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jPanel2, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(panelScreen2, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(panelScreen, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(panelFilter, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+ sidePanelLayout.setVerticalGroup(
+ sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(sidePanelLayout.createSequentialGroup()
+ .addGap(0, 0, 0)
+ .addComponent(jPanel5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(panelScreen2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(panelScreen, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(panelFilter, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+
+ toolBar.setFloatable(false);
+ toolBar.setRollover(true);
+
+ buttonSidePanel.setIcon(getIcon("List"));
+ buttonSidePanel.setSelected(true);
+ buttonSidePanel.setText(" ");
+ buttonSidePanel.setToolTipText("Show Side Panel");
+ buttonSidePanel.setFocusable(false);
+ buttonSidePanel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonSidePanel.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonSidePanelActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonSidePanel);
+
+ buttonStreamData.setIcon(getIcon("Details"));
+ buttonStreamData.setText(" ");
+ buttonStreamData.setToolTipText("Show Data Window");
+ buttonStreamData.setFocusable(false);
+ buttonStreamData.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonStreamData.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonStreamDataActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonStreamData);
+
+ buttonSave.setIcon(getIcon("Save"));
+ buttonSave.setText(" ");
+ buttonSave.setToolTipText("Save Snapshot");
+ buttonSave.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonSave.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonSaveActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonSave);
+
+ buttonGrabBackground.setIcon(getIcon("Background"));
+ buttonGrabBackground.setText(" ");
+ buttonGrabBackground.setToolTipText("Grab Background");
+ buttonGrabBackground.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonGrabBackground.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonGrabBackgroundActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonGrabBackground);
+
+ buttonPause.setIcon(getIcon("Pause"));
+ buttonPause.setText(" ");
+ buttonPause.setToolTipText("Pause");
+ buttonPause.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonPause.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonPauseActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonPause);
+
+ jSeparator6.setMaximumSize(new java.awt.Dimension(20, 32767));
+ jSeparator6.setPreferredSize(new java.awt.Dimension(20, 0));
+ jSeparator6.setRequestFocusEnabled(false);
+ toolBar.add(jSeparator6);
+
+ buttonMarker.setIcon(getIcon("Marker"));
+ buttonMarker.setText(" ");
+ buttonMarker.setToolTipText("Show Marker");
+ buttonMarker.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonMarker.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonMarkerActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonMarker);
+
+ buttonProfile.setIcon(getIcon("Profile"
+ + ""));
+ buttonProfile.setSelected(true);
+ buttonProfile.setText(" ");
+ buttonProfile.setToolTipText("Show Image Profile");
+ buttonProfile.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonProfile.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonProfileActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonProfile);
+
+ buttonFit.setIcon(getIcon("Fit"));
+ buttonFit.setSelected(true);
+ buttonFit.setText(" ");
+ buttonFit.setToolTipText("Show Fit");
+ buttonFit.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonFit.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonFitActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonFit);
+
+ buttonReticle.setIcon(getIcon("Reticule"));
+ buttonReticle.setSelected(true);
+ buttonReticle.setText(" ");
+ buttonReticle.setToolTipText("Show Reticle");
+ buttonReticle.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonReticle.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonReticleActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonReticle);
+
+ buttonTitle.setIcon(getIcon("Title"));
+ buttonTitle.setSelected(true);
+ buttonTitle.setText(" ");
+ buttonTitle.setToolTipText("Show Camera Name");
+ buttonTitle.setFocusable(false);
+ buttonTitle.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonTitle.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonTitleActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonTitle);
+
+ pauseSelection.setDecimals(0);
+
+ jLabel1.setText("Camera:");
+
+ comboCameras.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N
+ comboCameras.setMaximumRowCount(30);
+ comboCameras.setMinimumSize(new java.awt.Dimension(127, 27));
+ comboCameras.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ comboCamerasActionPerformed(evt);
+ }
+ });
+
+ jLabel5.setText("Type:");
+
+ comboType.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N
+ comboType.setMaximumRowCount(30);
+ comboType.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "All", "Laser", "Electrons", "Photonics" }));
+ comboType.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ comboTypeActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout panelCameraSelectionLayout = new javax.swing.GroupLayout(panelCameraSelection);
+ panelCameraSelection.setLayout(panelCameraSelectionLayout);
+ panelCameraSelectionLayout.setHorizontalGroup(
+ panelCameraSelectionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelCameraSelectionLayout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(jLabel1)
+ .addGap(0, 0, 0)
+ .addComponent(comboCameras, javax.swing.GroupLayout.PREFERRED_SIZE, 222, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jLabel5)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(comboType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(0, 0, 0))
+ );
+ panelCameraSelectionLayout.setVerticalGroup(
+ panelCameraSelectionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelCameraSelectionLayout.createSequentialGroup()
+ .addGap(0, 0, 0)
+ .addGroup(panelCameraSelectionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(comboType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel5)
+ .addComponent(jLabel1)
+ .addComponent(comboCameras, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGap(0, 0, 0))
+ );
+
+ javax.swing.GroupLayout topPanelLayout = new javax.swing.GroupLayout(topPanel);
+ topPanel.setLayout(topPanelLayout);
+ topPanelLayout.setHorizontalGroup(
+ topPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, topPanelLayout.createSequentialGroup()
+ .addComponent(toolBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(18, 18, 18)
+ .addComponent(panelCameraSelection, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGap(18, 18, 18)
+ .addComponent(pauseSelection, javax.swing.GroupLayout.PREFERRED_SIZE, 334, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap())
+ );
+ topPanelLayout.setVerticalGroup(
+ topPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(topPanelLayout.createSequentialGroup()
+ .addGap(1, 1, 1)
+ .addGroup(topPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(pauseSelection, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(toolBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(panelCameraSelection, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ );
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
+ .addComponent(sidePanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(renderer, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(topPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(topPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(sidePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(renderer, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
+ );
+ }// //GEN-END:initComponents
+
+ private void buttonZoomFitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonZoomFitActionPerformed
+ try {
+ renderer.setMode(RendererMode.Fit);
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonZoomFitActionPerformed
+
+ private void buttonZoomStretchActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonZoomStretchActionPerformed
+ try {
+ renderer.setMode(RendererMode.Stretch);
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonZoomStretchActionPerformed
+
+ private void buttonZoomNormalActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonZoomNormalActionPerformed
+ try {
+ renderer.setMode(RendererMode.Fixed);
+ centralizeRenderer();
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonZoomNormalActionPerformed
+
+ private void onChangeColormap(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onChangeColormap
+ try {
+ if ((camera != null) && (camera instanceof ColormapSource) && !updatingColormap) {
+ ColormapSource source = (ColormapSource) camera;
+ Color colorReticule = new Color(16, 16, 16);
+ Color colorMarker = new Color(128, 128, 128);
+ Colormap colormap = (Colormap) comboColormap.getSelectedItem();
+ source.getConfig().colormap = (colormap == null) ? Colormap.Flame : colormap;
+ switch (source.getConfig().colormap) {
+ case Grayscale:
+ case Inverted:
+ case Red:
+ case Green:
+ case Blue:
+ colorReticule = new Color(0, 192, 0);
+ colorMarker = new Color(64, 255, 64);
+ break;
+ case Flame:
+ colorReticule = new Color(0, 192, 0);
+ colorMarker = new Color(64, 255, 64);
+ break;
+ }
+
+ renderer.setPenReticle(new Pen(colorReticule));
+ renderer.setPenProfile(new Pen(colorReticule, 0));
+ renderer.setPenMarker(new Pen(colorMarker, 2));
+ renderer.setShowReticle(false);
+ checkReticle();
+ source.getConfig().colormapAutomatic = buttonAutomatic.isSelected();
+ source.getConfig().colormapMin = buttonFullRange.isSelected() ? Double.NaN : (Integer) spinnerMin.getValue();
+ source.getConfig().colormapMax = buttonFullRange.isSelected() ? Double.NaN : (Integer) spinnerMax.getValue();
+ try {
+ source.getConfig().save();
+ } catch (Exception ex) {
+ Logger.getLogger(getClass().getName()).log(Level.WARNING, null, ex);
+ }
+ source.refresh();
+ if (buttonPause.isSelected()) {
+ updatePause();
+ }
+ updateColormap();
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_onChangeColormap
+
+ private void onChangeColormapRange(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_onChangeColormapRange
+ onChangeColormap(null);
+ }//GEN-LAST:event_onChangeColormapRange
+
+ private void buttonZoom025ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonZoom025ActionPerformed
+ renderer.setZoom(0.25);
+ renderer.setMode(RendererMode.Zoom);
+ centralizeRenderer();
+ }//GEN-LAST:event_buttonZoom025ActionPerformed
+
+ private void buttonZoom05ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonZoom05ActionPerformed
+ renderer.setZoom(0.5);
+ renderer.setMode(RendererMode.Zoom);
+ centralizeRenderer();
+ }//GEN-LAST:event_buttonZoom05ActionPerformed
+
+ private void buttonServerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonServerActionPerformed
+ if (!usingServer) {
+ usingServer = true;
+ requestCameraListUpdate = true;
+ }
+ comboCamerasActionPerformed(null);
+ }//GEN-LAST:event_buttonServerActionPerformed
+
+ private void buttonDirectActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonDirectActionPerformed
+ if (usingServer) {
+ usingServer = false;
+ requestCameraListUpdate = true;
+ }
+ comboCamerasActionPerformed(null);
+ }//GEN-LAST:event_buttonDirectActionPerformed
+
+ private void comboScreenActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboScreenActionPerformed
+
+ comboScreen.setEnabled(false);
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ ChannelInteger setpoint = null;
+ try {
+ int index = comboScreen.getSelectedIndex();
+ if (index>=0){
+ if (cameraName.contains("DSRM")) {
+ setpoint = new ChannelInteger(null, cameraName + ":POSITION_SP");
+ } else {
+ setpoint = new ChannelInteger(null, cameraName + ":SET_SCREEN1_POS");
+ }
+ setpoint.initialize();
+ Integer readback = setpoint.read();
+ if ((readback==null) || (setpoint.read() != index)) {
+ setpoint.write(index);
+ //Must be threaded to control the laser because of sleep in setLaserState
+ /*
+ boolean laserOn = getLaserState();
+ if (laserOn) {
+ setLaserState(false);
+ }
+ try {
+ setpoint.write(index);
+ } finally {
+ if (laserOn) {
+ setLaserState(true);
+ }
+ }
+ */
+ }
+ screen.read();
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ } finally {
+ comboScreen.setEnabled(true);
+ if (setpoint != null) {
+ setpoint.close();
+ }
+ }
+ }
+ }).start();
+ }//GEN-LAST:event_comboScreenActionPerformed
+
+ private void comboFilterActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboFilterActionPerformed
+ try {
+ String setpoint = (String) comboFilter.getSelectedItem();
+ if (setpoint!=null){
+ if (!setpoint.equals(filter.read())) {
+ filter.write(setpoint);
+ }
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_comboFilterActionPerformed
+
+ private void checkHistogramActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkHistogramActionPerformed
+ try {
+ setHistogramVisible(checkHistogram.isSelected());
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_checkHistogramActionPerformed
+
+ private void buttonZoom2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonZoom2ActionPerformed
+ renderer.setZoom(2.0);
+ renderer.setMode(RendererMode.Zoom);
+ centralizeRenderer();
+ }//GEN-LAST:event_buttonZoom2ActionPerformed
+
+ private void spinnerThresholdonChange(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spinnerThresholdonChange
+ if (!updatingServerControls) {
+ try {
+ if ((server != null) && (server.isStarted())) {
+ server.setThreshold((Double) spinnerThreshold.getValue());
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ updatePipelineControls();
+ }
+ }
+ }//GEN-LAST:event_spinnerThresholdonChange
+
+ private void checkBackgroundActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkBackgroundActionPerformed
+ if (server != null) {
+ if (!updatingServerControls) {
+ try {
+ if (server.isStarted()) {
+ server.setBackgroundSubtraction(checkBackground.isSelected());
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ updatePipelineControls();
+ updatingServerControls = true;
+ checkBackground.setSelected(false);
+ updatingServerControls = false;
+
+ }
+ }
+ } else {
+ camera.setBackgroundEnabled(checkBackground.isSelected());
+ }
+ }//GEN-LAST:event_checkBackgroundActionPerformed
+
+ private void checkThresholdActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkThresholdActionPerformed
+ if (!updatingServerControls) {
+ try {
+ if ((server != null) && (server.isStarted())) {
+ spinnerThreshold.setVisible(checkThreshold.isSelected());
+ server.setThreshold(checkThreshold.isSelected() ? (Double) spinnerThreshold.getValue() : null);
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ updatePipelineControls();
+ }
+ }
+ }//GEN-LAST:event_checkThresholdActionPerformed
+
+ private void checkGoodRegionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkGoodRegionActionPerformed
+ if (!updatingServerControls) {
+ try {
+ if ((server != null) && (server.isStarted())) {
+ goodRegion = checkGoodRegion.isSelected();
+ setGoodRegionOptionsVisible(goodRegion);
+ if (goodRegion) {
+ server.setGoodRegion(((Number) spinnerGrThreshold.getValue()).doubleValue(), ((Number) spinnerGrScale.getValue()).doubleValue());
+ } else {
+ server.setGoodRegion(null);
+ }
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ updatePipelineControls();
+ }
+ }
+ }//GEN-LAST:event_checkGoodRegionActionPerformed
+
+ private void spinnerGrThresholdonChange(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spinnerGrThresholdonChange
+ if (!updatingServerControls) {
+ try {
+ if ((server != null) && (server.isStarted())) {
+ server.setGoodRegion((Double) spinnerGrThreshold.getValue(), (Double) spinnerGrScale.getValue());
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ updatePipelineControls();
+ }
+ }
+ }//GEN-LAST:event_spinnerGrThresholdonChange
+
+ private void btFixColormapRangeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btFixColormapRangeActionPerformed
+ try {
+ updatingColormap = true;
+ ArrayProperties properties = currentFrame.data.getProperties();
+ spinnerMax.setValue(properties.max.intValue());
+ spinnerMin.setValue(properties.min.intValue());
+ buttonManual.setSelected(true);
+ } catch (Exception ex) {
+ showException(ex);
+ } finally {
+ updatingColormap = false;
+ onChangeColormap(null);
+ }
+ }//GEN-LAST:event_btFixColormapRangeActionPerformed
+
+ private void checkSlicingActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkSlicingActionPerformed
+ if (!updatingServerControls) {
+ try {
+ if ((server != null) && (server.isStarted())) {
+ slicing = checkSlicing.isSelected();
+ setSlicingOptionsVisible(slicing);
+ if (slicing) {
+ server.setSlicing((Integer) spinnerSlNumber.getValue(), (Double) spinnerSlScale.getValue(), spinnerSlOrientation.getValue().toString());
+ } else {
+ server.setSlicing(null);
+ }
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ updatePipelineControls();
+ }
+ }
+ }//GEN-LAST:event_checkSlicingActionPerformed
+
+ private void spinnerSlicingChange(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spinnerSlicingChange
+ if (!updatingServerControls) {
+ try {
+ if ((server != null) && (server.isStarted())) {
+ server.setSlicing((Integer) spinnerSlNumber.getValue(), (Double) spinnerSlScale.getValue(), spinnerSlOrientation.getValue().toString());
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ updatePipelineControls();
+ }
+ }
+ }//GEN-LAST:event_spinnerSlicingChange
+
+ private void buttonReticleActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonReticleActionPerformed
+ try {
+ checkReticle();
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonReticleActionPerformed
+
+ private void buttonFitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonFitActionPerformed
+ try {
+ showFit = buttonFit.isSelected();
+ if (showFit) {
+ renderer.setProfile(Renderer.Profile.None);
+ } else {
+ renderer.removeOverlays(fitOv);
+ fitOv = null;
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonFitActionPerformed
+
+ private void buttonProfileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonProfileActionPerformed
+ try {
+ showProfile = buttonProfile.isSelected();
+ if (showProfile) {
+ renderer.setProfile(Renderer.Profile.None);
+ } else {
+ renderer.removeOverlays(profileOv);
+ profileOv = null;
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonProfileActionPerformed
+
+ private void buttonMarkerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonMarkerActionPerformed
+ try {
+ checkMarker(null);
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonMarkerActionPerformed
+
+ private void buttonPauseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonPauseActionPerformed
+ try {
+ if (!updatingButtons) {
+ removePauseOverlay();
+ if (camera != null) {
+ boolean pause = !renderer.isPaused();
+ synchronized (imageBuffer) {
+ if (pause) {
+ renderer.pause();
+ panelCameraSelection.setVisible(false);
+ pauseSelection.setVisible(true);
+ if (imageBuffer.size() > 0) {
+ pauseSelection.setEnabled(true);
+ pauseSelection.setMaxValue(imageBuffer.size());
+ pauseSelection.setValue(imageBuffer.size());
+ updatePause();
+ } else {
+ pauseSelection.setEnabled(false);
+ pauseSelection.setMaxValue(1);
+ pauseSelection.setValue(1);
+ }
+ } else {
+ imageBuffer.clear();
+ renderer.resume();
+ //renderer.clear();
+ pauseSelection.setVisible(false);
+ panelCameraSelection.setVisible(true);
+ }
+ }
+ updateStreamData();
+ }
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonPauseActionPerformed
+
+ private void buttonGrabBackgroundActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonGrabBackgroundActionPerformed
+ try {
+ if (camera != null) {
+ boolean laserOn = getLaserState();
+ OptionResult ret = null;
+ if (laserOn) {
+ ret = SwingUtils.showOption(getTopLevel(), "Capture Background", "Do you want to put laser on delay for capturing background?", OptionType.YesNoCancel);
+ if (ret == OptionResult.No) {
+ laserOn = false;
+ }
+ } else {
+ ret = SwingUtils.showOption(getTopLevel(), "Capture Background", "Do you want to capture background now?", OptionType.OkCancel);
+ }
+
+ if (ret == OptionResult.Cancel) {
+ return;
+ }
+
+ if (laserOn) {
+ setLaserState(false);
+ }
+ try {
+ System.out.println("Grabbing background for: " + cameraName);
+ if (server != null) {
+ server.captureBackground(5);
+ } else {
+ camera.captureBackground(5, 0);
+ }
+ } finally {
+ if (laserOn) {
+ setLaserState(true);
+ }
+ }
+ SwingUtils.showMessage(getTopLevel(), "Success", "Success capturing background", 5000);
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonGrabBackgroundActionPerformed
+
+ private void buttonSaveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonSaveActionPerformed
+ try {
+ saveSnapshot();
+ } catch (Exception ex) {
+ Logger.getLogger(getClass().getName()).log(Level.WARNING, null, ex);
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonSaveActionPerformed
+
+ private void buttonStreamDataActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonStreamDataActionPerformed
+ try {
+ showStreamData();
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonStreamDataActionPerformed
+
+ private void buttonSidePanelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonSidePanelActionPerformed
+ sidePanel.setVisible(buttonSidePanel.isSelected());
+ }//GEN-LAST:event_buttonSidePanelActionPerformed
+
+ private void comboTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboTypeActionPerformed
+ try {
+ if (!updatingCameraSelection) {
+ updateCameraList();
+ if ((cameraName != null) && (!cameraName.equals(comboCameras.getSelectedItem()))) {
+ setCamera(null);
+ } else {
+ setCamera(cameraName);
+ }
+ }
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ } finally {
+ updateButtons();
+ }
+ }//GEN-LAST:event_comboTypeActionPerformed
+
+ private void comboCamerasActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboCamerasActionPerformed
+ try {
+ if (!updatingCameraSelection) {
+ if (!comboCameras.isEnabled()) {
+ throw new Exception("Invalid state");
+ }
+ comboCameras.setEnabled(false);
+ comboType.setEnabled(false);
+ buttonServer.setEnabled(false);
+ buttonDirect.setEnabled(false);
+ final String cameraName = (String) comboCameras.getSelectedItem();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ if (requestCameraListUpdate) {
+ requestCameraListUpdate = false;
+ try {
+ updateCameraList();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ try {
+ setCamera(cameraName.trim().isEmpty() ? null : cameraName);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ } finally {
+ updateButtons();
+ comboCameras.setEnabled(true);
+ comboType.setEnabled(true);
+ buttonServer.setEnabled(true);
+ buttonDirect.setEnabled(true);
+ }
+ }
+ }).start();
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_comboCamerasActionPerformed
+
+ private void buttonTitleActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonTitleActionPerformed
+ try {
+ manageTitleOverlay();
+ } catch (Exception ex) {
+ showException(ex);
+ } finally {
+ }
+ }//GEN-LAST:event_buttonTitleActionPerformed
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton btFixColormapRange;
+ private javax.swing.JRadioButton buttonAutomatic;
+ private javax.swing.JRadioButton buttonDirect;
+ private javax.swing.JToggleButton buttonFit;
+ private javax.swing.JRadioButton buttonFullRange;
+ private javax.swing.JButton buttonGrabBackground;
+ private javax.swing.ButtonGroup buttonGroup1;
+ private javax.swing.ButtonGroup buttonGroup2;
+ private javax.swing.ButtonGroup buttonGroup3;
+ private javax.swing.ButtonGroup buttonGroup4;
+ private javax.swing.JRadioButton buttonManual;
+ private javax.swing.JToggleButton buttonMarker;
+ private javax.swing.JToggleButton buttonPause;
+ private javax.swing.JToggleButton buttonProfile;
+ private javax.swing.JToggleButton buttonReticle;
+ private javax.swing.JToggleButton buttonSave;
+ private javax.swing.JRadioButton buttonServer;
+ private javax.swing.JToggleButton buttonSidePanel;
+ private javax.swing.JButton buttonStreamData;
+ private javax.swing.JToggleButton buttonTitle;
+ private javax.swing.JRadioButton buttonZoom025;
+ private javax.swing.JRadioButton buttonZoom05;
+ private javax.swing.JRadioButton buttonZoom2;
+ private javax.swing.JRadioButton buttonZoomFit;
+ private javax.swing.JRadioButton buttonZoomNormal;
+ private javax.swing.JRadioButton buttonZoomStretch;
+ private javax.swing.JCheckBox checkBackground;
+ private javax.swing.JCheckBox checkGoodRegion;
+ private javax.swing.JCheckBox checkHistogram;
+ private javax.swing.JCheckBox checkSlicing;
+ private javax.swing.JCheckBox checkThreshold;
+ private javax.swing.JComboBox comboCameras;
+ private javax.swing.JComboBox comboColormap;
+ private javax.swing.JComboBox comboFilter;
+ private javax.swing.JComboBox comboScreen;
+ private javax.swing.JComboBox comboType;
+ private javax.swing.Box.Filler filler1;
+ private javax.swing.JLabel jLabel1;
+ private javax.swing.JLabel jLabel3;
+ private javax.swing.JLabel jLabel4;
+ private javax.swing.JLabel jLabel5;
+ private javax.swing.JPanel jPanel2;
+ private javax.swing.JPanel jPanel3;
+ private javax.swing.JPanel jPanel5;
+ private javax.swing.JProgressBar jProgressBar1;
+ private javax.swing.JToolBar.Separator jSeparator6;
+ private javax.swing.JLabel labelGrScale;
+ private javax.swing.JLabel labelGrThreshold;
+ private javax.swing.JLabel labelMax;
+ private javax.swing.JLabel labelMin;
+ private javax.swing.JLabel labelSlNumber;
+ private javax.swing.JLabel labelSlOrientation;
+ private javax.swing.JLabel labelSlScale;
+ private javax.swing.JPanel panelCameraSelection;
+ private javax.swing.JPanel panelFilter;
+ private javax.swing.JPanel panelScreen;
+ private javax.swing.JPanel panelScreen2;
+ private javax.swing.JPanel panelSlicing;
+ private ch.psi.pshell.swing.ValueSelection pauseSelection;
+ private ch.psi.pshell.imaging.Renderer renderer;
+ private javax.swing.JPanel sidePanel;
+ private javax.swing.JSpinner spinnerGrScale;
+ private javax.swing.JSpinner spinnerGrThreshold;
+ private javax.swing.JSpinner spinnerMax;
+ private javax.swing.JSpinner spinnerMin;
+ private javax.swing.JSpinner spinnerSlNumber;
+ private javax.swing.JSpinner spinnerSlOrientation;
+ private javax.swing.JSpinner spinnerSlScale;
+ private javax.swing.JSpinner spinnerThreshold;
+ private javax.swing.JTextField textState;
+ private javax.swing.JToolBar toolBar;
+ private javax.swing.JPanel topPanel;
+ private ch.psi.pshell.swing.DeviceValuePanel valueFilter;
+ private ch.psi.pshell.swing.DeviceValuePanel valueScreen;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/plugins/ScreenPanel3OLD.form b/plugins/ScreenPanel3OLD.form
new file mode 100644
index 0000000..bc4a4f3
--- /dev/null
+++ b/plugins/ScreenPanel3OLD.form
@@ -0,0 +1,1164 @@
+
+
+
diff --git a/plugins/ScreenPanel3OLD.java b/plugins/ScreenPanel3OLD.java
new file mode 100644
index 0000000..1705b70
--- /dev/null
+++ b/plugins/ScreenPanel3OLD.java
@@ -0,0 +1,3934 @@
+/*
+ * Copyright (c) 2014 Paul Scherrer Institute. All rights reserved.
+ */
+
+import ch.psi.pshell.bs.CameraServer;
+import ch.psi.pshell.core.Context;
+import java.io.IOException;
+import java.nio.file.Paths;
+import javax.swing.DefaultComboBoxModel;
+import ch.psi.pshell.ui.Panel;
+import ch.psi.pshell.imaging.ImageListener;
+import ch.psi.utils.State;
+import ch.psi.utils.Chrono;
+import ch.psi.utils.swing.SwingUtils;
+import ch.psi.utils.swing.TextEditor;
+import ch.psi.pshell.bs.PipelineServer;
+import ch.psi.pshell.bs.StreamValue;
+import ch.psi.pshell.core.JsonSerializer;
+import ch.psi.pshell.data.DataManager;
+import ch.psi.pshell.device.Device;
+import ch.psi.pshell.device.Readable.ReadableArray;
+import ch.psi.pshell.device.Readable.ReadableNumber;
+import ch.psi.pshell.device.ReadableRegister.ReadableRegisterArray;
+import ch.psi.pshell.device.ReadableRegister.ReadableRegisterNumber;
+import ch.psi.pshell.epics.ChannelInteger;
+import ch.psi.pshell.epics.DiscretePositioner;
+import ch.psi.pshell.epics.Epics;
+import ch.psi.pshell.imaging.Colormap;
+import ch.psi.pshell.imaging.ColormapSource;
+import ch.psi.pshell.imaging.ColormapSource.ColormapSourceConfig;
+import ch.psi.pshell.ui.App;
+import ch.psi.pshell.imaging.Data;
+import ch.psi.pshell.imaging.DimensionDouble;
+import ch.psi.pshell.imaging.Histogram;
+import ch.psi.pshell.imaging.ImageBuffer;
+import ch.psi.pshell.imaging.Overlay;
+import ch.psi.pshell.imaging.Overlays;
+import ch.psi.pshell.imaging.Overlays.Text;
+import ch.psi.pshell.imaging.Pen;
+import ch.psi.pshell.imaging.PointDouble;
+import ch.psi.pshell.imaging.Renderer;
+import ch.psi.pshell.imaging.RendererListener;
+import ch.psi.pshell.imaging.RendererMode;
+import ch.psi.pshell.imaging.Source;
+import ch.psi.pshell.scripting.InterpreterResult;
+import ch.psi.pshell.scripting.ScriptManager;
+import ch.psi.pshell.swing.DeviceValueChart;
+import ch.psi.pshell.swing.ValueSelection;
+import ch.psi.pshell.swing.ValueSelection.ValueSelectionListener;
+import ch.psi.pshell.ui.Console;
+import ch.psi.utils.Arr;
+import ch.psi.utils.ArrayProperties;
+import ch.psi.utils.Convert;
+import ch.psi.utils.Str;
+import ch.psi.utils.swing.Editor.EditorDialog;
+import ch.psi.utils.swing.MainFrame;
+import ch.psi.utils.swing.StandardDialog;
+import ch.psi.utils.swing.StandardDialog.StandardDialogListener;
+import ch.psi.utils.swing.SwingUtils.OptionResult;
+import ch.psi.utils.swing.SwingUtils.OptionType;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.Transferable;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileInputStream;
+import java.lang.reflect.Array;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSpinner;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+import javax.swing.table.DefaultTableModel;
+import org.apache.commons.math3.analysis.function.Gaussian;
+import org.apache.commons.math3.fitting.GaussianCurveFitter;
+import org.apache.commons.math3.fitting.PolynomialCurveFitter;
+import org.apache.commons.math3.fitting.WeightedObservedPoint;
+import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
+
+/**
+ *
+ */
+public class ScreenPanel3OLD extends Panel {
+
+ public static final String LASER_TYPE = "Laser";
+ public static final String ELECTRONS_TYPE = "Electrons";
+ public static final String PHOTONICS_TYPE = "Photonics";
+
+ final String CAMERA_DEVICE_NAME = "CurrentCamera";
+ boolean useServerStats = true;
+ String userOverlaysConfigFile;
+ ColormapSource camera;
+ PipelineServer server;
+ String cameraName;
+ int polling = 1000;
+ Overlay marker = null;
+ JDialog histogramDialog;
+ DiscretePositioner screen;
+ DiscretePositioner filter;
+ boolean showFit;
+ boolean showProfile;
+ Overlay[] userOv;
+ Overlay[] fitOv;
+ Overlay[] profileOv;
+ Overlay errorOverlay;
+ boolean requestCameraListUpdate;
+ boolean goodRegion;
+ boolean slicing;
+ String serverUrl;
+ String camServerUrl;
+ String instanceName;
+ Overlay titleOv = null;
+ int integration = 0;
+
+ String pipelineSuffix = "_sp";
+
+ Double getServerDouble(String name) {
+ return (Double) Convert.toDouble(server.getValue(name));
+ }
+
+ double[] getServerDoubleArray(String name) {
+ return (double[]) Convert.toDouble(server.getValue(name));
+ }
+
+ Double getServerDouble(String name, StreamValue cache) {
+ return (Double) Convert.toDouble(cache.__getitem__(name));
+ }
+
+ double[] getServerDoubleArray(String name, StreamValue cache) {
+ return (double[]) Convert.toDouble(cache.__getitem__(name));
+ }
+
+ class ImageData {
+
+ ImageData() {
+ if (server != null) {
+ cache = server.getStream().take();
+ String prefix = goodRegion ? "gr_" : "";
+ x_fit_mean = getServerDouble(prefix + "x_fit_mean", cache);
+ y_fit_mean = getServerDouble(prefix + "y_fit_mean", cache);
+ x_fit_standard_deviation = getServerDouble(prefix + "x_fit_standard_deviation", cache);
+ y_fit_standard_deviation = getServerDouble(prefix + "y_fit_standard_deviation", cache);
+ x_fit_gauss_function = getServerDoubleArray(prefix + "x_fit_gauss_function", cache);
+ y_fit_gauss_function = getServerDoubleArray(prefix + "y_fit_gauss_function", cache);
+ x_profile = getServerDoubleArray("x_profile", cache);
+ y_profile = getServerDoubleArray("y_profile", cache);
+ x_center_of_mass = getServerDouble("x_center_of_mass", cache);
+ y_center_of_mass = getServerDouble("y_center_of_mass", cache);
+ x_rms = getServerDouble("x_rms", cache);
+ y_rms = getServerDouble("y_rms", cache);
+ if (goodRegion) {
+ double[] gX2 = new double[x_profile.length];
+ Arrays.fill(gX2, Double.NaN);
+ try {
+ double x = getServerDoubleArray("gr_x_axis", cache)[0];
+ gr_size_x = x_fit_gauss_function.length;
+ gr_pos_x = (int) ((renderer.getCalibration() != null) ? renderer.getCalibration().convertToImageX(x) : x);
+ System.arraycopy(x_fit_gauss_function, 0, gX2, gr_pos_x, gr_size_x);
+ } catch (Exception ex) {
+ }
+ x_fit_gauss_function = gX2;
+ double[] gY2 = new double[y_profile.length];
+ Arrays.fill(gY2, Double.NaN);
+ try {
+ double y = getServerDoubleArray("gr_y_axis", cache)[0];
+ gr_size_y = y_fit_gauss_function.length;
+ gr_pos_y = (int) ((renderer.getCalibration() != null) ? renderer.getCalibration().convertToImageY(y) : y);
+ System.arraycopy(y_fit_gauss_function, 0, gY2, gr_pos_y, y_fit_gauss_function.length);
+ } catch (Exception ex) {
+ }
+ y_fit_gauss_function = gY2;
+ if (slicing) {
+ try {
+ int slices = getServerDouble("slice_amount").intValue();
+ sliceCenters = new PointDouble[slices];
+ for (int i = 0; i < slices; i++) {
+ double x = getServerDouble("slice_" + i + "_center_x");
+ double y = getServerDouble("slice_" + i + "_center_y");
+ sliceCenters[i] = new PointDouble(x, y);
+ }
+ } catch (Exception ex) {
+ }
+ }
+ }
+ }
+ }
+ public Double x_fit_mean;
+ public Double y_fit_mean;
+ public Double x_center_of_mass;
+ public Double x_rms;
+ public Double x_fit_standard_deviation;
+ public Double y_fit_standard_deviation;
+ public Double y_center_of_mass;
+ public Double y_rms;
+ public double[] x_profile;
+ public double[] x_fit_gauss_function;
+ public double[] y_profile;
+ public double[] y_fit_gauss_function;
+ public int gr_size_x;
+ public int gr_pos_x;
+ public int gr_size_y;
+ public int gr_pos_y;
+ public PointDouble[] sliceCenters;
+ public StreamValue cache;
+ }
+
+ class Frame extends ImageData {
+
+ Frame(Data data) {
+ this.data = data;
+ }
+ Data data;
+ }
+
+ final ArrayList imageBuffer = new ArrayList();
+ Frame currentFrame;
+ int imageBufferLenght = 1;
+ Text imagePauseOverlay;
+ final Console console;
+
+ public ScreenPanel3OLD() {
+ try {
+ initComponents();
+ spinnerThreshold.setVisible(false);
+ btFixColormapRange.setVisible(false);
+ setGoodRegionOptionsVisible(false);
+ setSlicingOptionsVisible(false);
+ JComponent editor = spinnerSlOrientation.getEditor();
+ if (editor instanceof JSpinner.DefaultEditor) {
+ ((JSpinner.DefaultEditor) editor).getTextField().setHorizontalAlignment(JTextField.RIGHT);
+ }
+ renderer.setPersistenceFile(Paths.get(getContext().getSetup().getContextPath(), "Renderer_Cameras.bin"));
+ //setPersistedComponents(new Component[]{buttonServer, buttonDirect});
+ comboCameras.setEnabled(false);
+ comboType.setEnabled(false);
+
+ SwingUtils.setEnumCombo(comboColormap, Colormap.class);
+ if (App.hasArgument("poll")) {
+ try {
+ polling = Integer.valueOf(App.getArgumentValue("poll"));
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ if (App.hasArgument("zoom")) {
+ try {
+ renderer.setDefaultZoom(Double.valueOf(App.getArgumentValue("zoom")));
+ renderer.resetZoom();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ if (App.hasArgument("buf")) {
+ try {
+ imageBufferLenght = Integer.valueOf(App.getArgumentValue("buf"));
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ if (App.hasArgument("usr_ov")) {
+ try {
+ userOverlaysConfigFile = App.getArgumentValue("usr_ov");
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ if (App.hasArgument("srv_url")) {
+ serverUrl = App.getArgumentValue("srv_url");
+ }
+
+ if (App.hasArgument("cam_srv_url")) {
+ camServerUrl = App.getArgumentValue("cam_srv_url");
+ }
+
+ if (App.hasArgument("calc")) {
+ useServerStats = false;
+ }
+ if (App.hasArgument("suffix")) {
+ pipelineSuffix = App.getArgumentValue("suffix");
+ }
+ if (App.hasArgument("integration")) {
+ try {
+ setIntegration(Integer.valueOf(App.getArgumentValue("integration")));
+ if (integration != 0) {
+ buttonFit.setSelected(false);
+ buttonProfile.setSelected(false);
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ renderer.setProfileNormalized(true);
+ renderer.setShowProfileLimits(false);
+
+ JMenuItem menuCalibrate = new JMenuItem("Calibrate...");
+ menuCalibrate.addActionListener((ActionEvent e) -> {
+ try {
+ calibrate();
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ });
+
+ JMenuItem menuRendererConfig = new JMenuItem("Renderer Parameters");
+ menuRendererConfig.addActionListener((ActionEvent e) -> {
+ try {
+ if (camera != null) {
+ this.showDeviceConfigDialog(camera, false);
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ });
+
+ JMenuItem menuCameraConfig = new JMenuItem("Camera Configurarion");
+ menuCameraConfig.addActionListener((ActionEvent e) -> {
+ try {
+ if (camera != null) {
+ String cameraConfigJson = null;
+ if (usingServer) {
+ String cameraServerUrl = (camServerUrl == null) ? server.getUrl().substring(0, server.getUrl().length() - 1) + "8" : camServerUrl;
+ try (CameraServer srv = new CameraServer("CamServer", cameraServerUrl)) {
+ srv.initialize();
+ //TODO: replace into encodeMultiline
+ cameraConfigJson = JsonSerializer.encode(srv.getConfig(cameraName), true);
+ }
+
+ } else {
+ String configFolder = (String) getContext().getClassByName("SfCamera").getMethod("getConfigFolder", new Class[]{}).invoke(null);
+ Path configFile = Paths.get(configFolder, cameraName + ".json");
+ cameraConfigJson = configFile.toFile().exists() ? new String(Files.readAllBytes(configFile)) : null;
+ }
+ TextEditor configEditor = new TextEditor();
+ configEditor.setText(cameraConfigJson);
+ configEditor.setReadOnly(true);
+ configEditor.setTitle(cameraName);
+ EditorDialog dlg = configEditor.getDialog(getTopLevel(), false);
+ dlg.setSize(480, 640);
+ dlg.setVisible(true);
+ SwingUtils.centerComponent(getTopLevel(), dlg);
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ });
+
+ JMenuItem menuSetImageBufferSize = new JMenuItem("Set Stack Size...");
+ menuSetImageBufferSize.addActionListener((ActionEvent e) -> {
+ try {
+ String ret = SwingUtils.getString(getTopLevel(), "Enter size of image buffer: ", String.valueOf(imageBufferLenght));
+ if (ret != null) {
+ this.setImageBufferSize(Integer.valueOf(ret));
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ });
+
+ JMenuItem menuSaveStack = new JMenuItem("Save Stack");
+ menuSaveStack.addActionListener((ActionEvent e) -> {
+ try {
+ saveStack();
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ });
+
+ JMenuItem menuSetROI = new JMenuItem("Set ROI...");
+ menuSetROI.addActionListener((ActionEvent e) -> {
+ renderer.abortSelection();
+ if (server != null) {
+ final Overlays.Rect selection = new Overlays.Rect(renderer.getPenMovingOverlay());
+ renderer.addListener(new RendererListener() {
+ @Override
+ public void onSelectionFinished(Renderer renderer, Overlay overlay) {
+ try {
+ renderer.setShowReticle(false);
+ Rectangle roi = overlay.isFixed() ? renderer.toImageCoord(overlay.getBounds()) : overlay.getBounds();
+ if (server.isRoiEnabled()) {
+ int[] cur = server.getRoi();
+ server.setRoi(new int[]{roi.x + cur[0], roi.y + cur[1], roi.width, roi.height});
+ } else {
+ server.setRoi(new int[]{roi.x, roi.y, roi.width, roi.height});
+ }
+ } catch (Exception ex) {
+ } finally {
+ renderer.removeListener(this);
+ }
+ }
+
+ @Override
+ public void onSelectionAborted(Renderer renderer, Overlay overlay) {
+ renderer.removeListener(this);
+ }
+ });
+ selection.setFixed(true);
+ renderer.startSelection(selection);
+ }
+ });
+
+ JMenuItem menuResetROI = new JMenuItem("Reset ROI");
+ menuResetROI.addActionListener((ActionEvent e) -> {
+ renderer.abortSelection();
+ if (server != null) {
+ try {
+ renderer.setShowReticle(false);
+ server.resetRoi();
+ } catch (IOException ex) {
+ showException(ex);
+ }
+ }
+ });
+
+ JCheckBoxMenuItem menuFrameIntegration = new JCheckBoxMenuItem("Multi-Frame", (integration != 0));
+ menuFrameIntegration.addActionListener((ActionEvent e) -> {
+ if (integration == 0) {
+ JPanel panel = new JPanel();
+ GridBagLayout layout = new GridBagLayout();
+ layout.columnWidths = new int[]{150, 50}; //Minimum width
+ layout.rowHeights = new int[]{30, 30}; //Minimum height
+ panel.setLayout(layout);
+ JCheckBox checkContinuous = new JCheckBox("");
+ JTextField textFrames = new JTextField();
+ GridBagConstraints c = new GridBagConstraints();
+ c.gridx = 0;
+ c.gridy = 0;
+ panel.add(new JLabel("Number of frames:"), c);
+ c.gridy = 1;
+ panel.add(new JLabel("Continuous:"), c);
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridx = 1;
+ panel.add(checkContinuous, c);
+ c.gridy = 0;
+ panel.add(textFrames, c);
+ if (SwingUtils.showOption(getTopLevel(), "Multi-Frame Integration", panel, OptionType.OkCancel) == OptionResult.Yes) {
+ setIntegration(checkContinuous.isSelected() ? -(Integer.valueOf(textFrames.getText())) : (Integer.valueOf(textFrames.getText())));
+ }
+ } else {
+ if (SwingUtils.showOption(getTopLevel(), "Multi-Frame Integration",
+ "Do you want to disable " + ((integration < 0) ? "continuous " : "") + "multi-frame integration (" + Math.abs(integration) + ")?", OptionType.YesNo) == OptionResult.Yes) {
+ setIntegration(0);
+ }
+ }
+ });
+
+ for (Component cmp : SwingUtils.getComponentsByType(renderer.getPopupMenu(), JMenu.class)) {
+ JMenu menu = (JMenu) cmp;
+ if (menu.getText().equals("Integration")) {
+ menu.addSeparator();
+ menu.add(menuFrameIntegration);
+ }
+ }
+ renderer.getPopupMenu().addSeparator();
+ renderer.getPopupMenu().add(menuRendererConfig);
+ renderer.getPopupMenu().add(menuCameraConfig);
+ renderer.getPopupMenu().add(menuSetImageBufferSize);
+ renderer.getPopupMenu().add(menuSaveStack);
+ renderer.getPopupMenu().addSeparator();
+ renderer.getPopupMenu().add(menuCalibrate);
+ renderer.getPopupMenu().addSeparator();
+ renderer.getPopupMenu().add(menuSetROI);
+ renderer.getPopupMenu().add(menuResetROI);
+ renderer.getPopupMenu().addPopupMenuListener(new PopupMenuListener() {
+ @Override
+ public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
+ menuResetROI.setEnabled(server != null);
+ menuSetROI.setEnabled(server != null);
+ menuCalibrate.setVisible(server != null);
+ menuCalibrate.setEnabled((calibrationDialolg == null) || (!calibrationDialolg.isShowing()));
+ menuSaveStack.setEnabled(imageBufferLenght > 0);
+ menuSetImageBufferSize.setEnabled(!renderer.isPaused());
+ menuFrameIntegration.setSelected(integration != 0);
+ }
+
+ @Override
+ public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
+ }
+
+ @Override
+ public void popupMenuCanceled(PopupMenuEvent e) {
+ }
+ });
+ renderer.getPopupMenu().setVisible(false);
+ clearMarker();
+
+ showFit = buttonFit.isSelected();
+ showProfile = buttonProfile.isSelected();
+
+ pauseSelection.setVisible(false);
+ pauseSelection.setMinValue(1);
+ pauseSelection.addListener(new ValueSelectionListener() {
+ @Override
+ public void onValueChanged(ValueSelection origin, double value, boolean editing) {
+ if (editing && (value >= 1) && (value <= imageBuffer.size())) {
+ updatePause();
+ }
+ }
+ });
+ renderer.addListener(new RendererListener() {
+ @Override
+ public void onMoveFinished(Renderer renderer, Overlay overlay) {
+ if (overlay == marker) {
+ try {
+ onMarkerChanged();
+ } catch (IOException ex) {
+ Logger.getLogger(ScreenPanel3OLD.class.getName()).log(Level.WARNING, null, ex);
+ }
+ }
+ }
+ });
+ if (MainFrame.isDark()) {
+ textState.setDisabledTextColor(textState.getForeground());
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ void setIntegration(int frames) {
+ try {
+ if (integration != frames) {
+ integration = frames;
+ if (camera != null) {
+ if (Math.abs(integration) > 1) {
+ renderer.setDevice(new ImageIntegrator(integration));
+ } else {
+ renderer.setDevice(camera);
+ }
+ synchronized (imageBuffer) {
+ currentFrame = null;
+ imageBuffer.clear();
+ }
+ }
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (App.hasArgument("ct")) {
+ boolean direct = App.getArgumentValue("ct").equals("0") || App.getArgumentValue("ct").equalsIgnoreCase("false");
+ buttonServer.setSelected(!direct);
+ buttonDirect.setSelected(direct);
+ }
+ }
+
+ @Override
+ public void onStop() {
+ try {
+ if (camera != null) {
+ camera.close();
+ camera = null;
+ server = null;
+ updateButtons();
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ super.onStop();
+ }
+
+ //Overridable callbacks
+ @Override
+ public void onInitialize(int runCount) {
+ comboCameras.setEnabled(false);
+ comboType.setEnabled(false);
+ if (App.hasArgument("s")) {
+ renderer.setDevice((Source) getDevice("image"));
+ renderer.setAutoScroll(true);
+ ((Source) getDevice("image")).addListener(new ImageListener() {
+ @Override
+ public void onImage(Object o, BufferedImage bi, Data data) {
+ manageFit(bi, data);
+ manageUserOverlays(bi, data);
+ }
+
+ @Override
+ public void onError(Object o, Exception ex) {
+ }
+ }
+ );
+
+ } else {
+ usingServer = buttonServer.isSelected();
+ updateCameraList();
+ comboCameras.setEnabled(true);
+ comboType.setEnabled(true);
+ setComboCameraSelection(-1);
+ setComboTypeSelection(0);
+
+ if (comboCameras.getModel().getSize() > 0) {
+ try {
+ if (App.hasArgument("cam")) {
+ setComboCameraSelection(App.getArgumentValue("cam"));
+ comboCamerasActionPerformed(null);
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+ updateButtons();
+ startTimer(1000);
+ }
+
+ boolean isVisible(String camera) {
+ return ((comboType.getSelectedIndex() == 0) || (getCameraType(camera).equals(comboType.getSelectedItem())));
+ }
+
+ DefaultComboBoxModel getCameraList(boolean fromServer) throws Exception {
+ DefaultComboBoxModel model = new DefaultComboBoxModel();
+ if (fromServer) {
+ try (PipelineServer srv = newServer()) {
+ srv.initialize();
+ List cameras = srv.getCameras();
+ Collections.sort(cameras);
+ for (String camera : cameras) {
+ if (isVisible(camera)) {
+ model.addElement(camera);
+ }
+ }
+ }
+
+ } else {
+ ArrayList cameras = (ArrayList) getContext().getClassByName("SfCamera").getMethod("getCameras", new Class[]{}).invoke(null);
+ for (String camera : cameras) {
+ if (isVisible(camera)) {
+ model.addElement(camera);
+ }
+ }
+ }
+ if (App.hasArgument("cam")) {
+ String camera = App.getArgumentValue("cam");
+ if (model.getIndexOf(camera) < 0) {
+ if (isVisible(camera)) {
+ model.addElement(camera);
+ }
+ }
+ }
+ model.addElement("");
+
+ return model;
+ }
+
+ PipelineServer newServer() throws IOException {
+ if (serverUrl != null) {
+ System.out.println("Connecting to server: " + serverUrl);
+ server = new PipelineServer(CAMERA_DEVICE_NAME, serverUrl);
+ } else {
+ System.out.println("Connecting to server");
+ server = new PipelineServer(CAMERA_DEVICE_NAME);
+ }
+ updateButtons();
+ return server;
+ }
+
+ boolean updatingCameraSelection;
+
+ void setComboCameraSelection(Object selection) {
+ updatingCameraSelection = true;
+ try {
+ comboCameras.setSelectedItem(selection);
+ } finally {
+ updatingCameraSelection = false;
+ }
+ }
+
+ void setComboTypeSelection(Object selection) {
+ updatingCameraSelection = true;
+ try {
+ comboType.setSelectedItem(selection);
+ } finally {
+ updatingCameraSelection = false;
+ }
+ }
+ boolean usingServer;
+
+ void updateCameraList() {
+ try {
+ String selected = (String) comboCameras.getSelectedItem();
+ comboCameras.setModel(getCameraList(usingServer));
+ if (selected != null) {
+ if (((DefaultComboBoxModel) comboCameras.getModel()).getIndexOf(camera) < 0) {
+ setComboCameraSelection(selected);
+ }
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ } finally {
+ updateButtons();
+ }
+ }
+
+ final Object lockOverlays = new Object();
+
+ void manageFit(BufferedImage bi, Data data) {
+ Overlay[][] fo = null;
+ if ((showFit || showProfile)) {
+ try {
+ fo = getFitOverlays(data);
+ } catch (Exception ex) {
+ System.err.println(ex);
+ }
+ }
+ synchronized (lockOverlays) {
+ fo = (fo == null) ? new Overlay[][]{null, null} : fo;
+ renderer.updateOverlays(fo[0], profileOv);
+ profileOv = fo[0];
+ renderer.updateOverlays(fo[1], fitOv);
+ fitOv = fo[1];
+ }
+ }
+
+ void manageUserOverlays(BufferedImage bi, Data data) {
+ Overlay[] fo = (bi == null) ? null : getUserOverlays(data);
+ synchronized (lockOverlays) {
+ renderer.updateOverlays(fo, userOv);
+ userOv = fo;
+ }
+ }
+
+ void manageTitleOverlay() {
+ Overlay to = null;
+ if ((buttonTitle.isSelected()) && (cameraName != null)) {
+ Font font = new Font("Arial", Font.PLAIN, 28);
+ to = new Text(renderer.getPenErrorText(), cameraName, font, new Point(-SwingUtils.getTextSize(cameraName, renderer.getGraphics().getFontMetrics(font)).width - 14, 26));
+ to.setFixed(true);
+ to.setAnchor(Overlay.ANCHOR_VIEWPORT_OR_IMAGE_TOP_RIGHT);
+ }
+
+ synchronized (lockOverlays) {
+ renderer.updateOverlays(to, titleOv);
+ titleOv = to;
+ }
+ }
+
+ @Override
+ public void onStateChange(State state, State former) {
+
+ }
+
+ @Override
+ public void onExecutedFile(String fileName, Object result) {
+ }
+
+ //Callback to perform update - in event thread
+ @Override
+ protected void doUpdate() {
+ }
+
+ Thread devicesInitTask;
+
+ void setCamera(String cameraName) throws IOException, InterruptedException {
+ System.out.println("Initializing");
+ parseUserOverlays();
+ errorOverlay = null;
+ lastMarkerPos = null;
+
+ if (dataTableDialog != null) {
+ dataTableDialog.dispose();
+ dataTableDialog = null;
+ }
+ dataTableModel = null;
+
+ if (calibrationDialolg != null) {
+ calibrationDialolg.dispose();
+ calibrationDialolg = null;
+ }
+
+ boolean was_server = false;
+ if (camera != null) {
+ //camera.removeAllListeners();
+ was_server = (server != null);
+ camera.close();
+ camera = null;
+ server = null;
+ }
+ updateButtons();
+ instanceName = null;
+ renderer.setDevice(null);
+ renderer.setShowReticle(false);
+ renderer.removeOverlays(fitOv);
+ renderer.removeOverlays(profileOv);
+ renderer.removeOverlays(userOv);
+ renderer.clear();
+ renderer.resetZoom();
+
+ boolean changed = !String.valueOf(cameraName).equals(this.cameraName);
+ this.cameraName = cameraName;
+
+ if (changed || buttonDirect.isSelected()) {
+ spinnerThreshold.setVisible(false);
+ checkThreshold.setEnabled(false);
+ checkGoodRegion.setEnabled(false);
+ setGoodRegionOptionsVisible(false);
+ setSlicingOptionsVisible(false);
+ }
+ synchronized (imageBuffer) {
+ currentFrame = null;
+ imageBuffer.clear();
+ }
+ if (changed) {
+ checkBackground.setEnabled(false);
+ if ((devicesInitTask != null) && (devicesInitTask.isAlive())) {
+ devicesInitTask.interrupt();
+ }
+ if (screen != null) {
+ screen.close();
+ screen = null;
+ }
+ if (filter != null) {
+ filter.close();
+ filter = null;
+ }
+ if (renderer.isPaused()) {
+ renderer.resume();
+ removePauseOverlay();
+ pauseSelection.setVisible(false);
+ panelCameraSelection.setVisible(true);
+ }
+ }
+ manageTitleOverlay();
+ if (App.isDetached()) {
+ getTopLevel().setTitle(cameraName == null ? "ScreenPanel" : cameraName);
+ }
+ if (cameraName == null) {
+ return;
+ }
+
+ System.out.println("Setting camera: " + cameraName + " [" + (buttonServer.isSelected() ? "server" : "direct") + "]");
+ try {
+ if (buttonServer.isSelected()) {
+ camera = newServer();
+ camera.getConfig().flipHorizontally = false;
+ camera.getConfig().flipVertically = false;
+ camera.getConfig().rotation = 0.0;
+ camera.getConfig().roiX = 0;
+ camera.getConfig().roiY = 0;
+ camera.getConfig().roiWidth = -1;
+ camera.getConfig().roiHeight = -1;
+ } else {
+ //camera = new SfCamera(CAMERA_DEVICE_NAME, cameraName);
+ camera = (ColormapSource) getContext().getClassByName("SfCamera").getConstructor(new Class[]{String.class, String.class}).newInstance(new Object[]{CAMERA_DEVICE_NAME, cameraName});
+ }
+ camera.initialize();
+ camera.assertInitialized();
+ System.out.println("Camera initialization OK");
+ if (server != null) {
+ //server.start(cameraName, false);
+ String pipelineName = cameraName + pipelineSuffix;
+ instanceName = cameraName + pipelineSuffix + "1";
+ if (!server.getPipelines().contains(pipelineName)) {
+ System.out.println("Creating pipeline: " + pipelineName);
+ HashMap config = new HashMap<>();
+ config.put("camera_name", cameraName);
+ //server.createFromConfig(config, pipelineName);
+ server.savePipelineConfig(pipelineName, config);
+ }
+ server.start(pipelineName, instanceName);
+
+ updateServerControls();
+ checkThreshold.setEnabled(true);
+ checkGoodRegion.setEnabled(true);
+ } else {
+ checkThreshold.setSelected(false);
+ checkGoodRegion.setSelected(false);
+ if (polling <= 0) {
+ camera.setMonitored(true);
+ } else {
+ camera.setPolling(polling);
+ }
+ camera.setBackgroundEnabled(checkBackground.isSelected());
+ }
+ updateButtons();
+ camera.getConfig().save();
+ if (Math.abs(integration) > 1) {
+ renderer.setDevice(new ImageIntegrator(integration));
+ } else {
+ renderer.setDevice(camera);
+ }
+ renderer.setAutoScroll(true);
+ //renderer.setMarker(marker);
+ clearMarker();
+ imageSize = null;
+
+ camera.addListener(new ImageListener() {
+ @Override
+ public void onImage(Object o, BufferedImage bi, Data data) {
+ if (bi != null) {
+ if ((imageSize == null) || imageSize.width != bi.getWidth() || imageSize.height != bi.getHeight()) {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ if ((renderer.getMode() == RendererMode.Zoom) || (renderer.getMode() == RendererMode.Fixed)) {
+ centralizeRenderer();
+ }
+ checkReticle();
+ }
+ });
+ imageSize = new Dimension(bi.getWidth(), bi.getHeight());
+ }
+ renderer.setProfileSize(Math.min(bi.getWidth(), bi.getHeight()));
+ }
+ //renderer.setCalibration(camera.getCalibration());
+ if (!renderer.isPaused()) {
+ if (data != null) {
+ synchronized (imageBuffer) {
+ currentFrame = new Frame(data);
+ if (imageBufferLenght >= 1) {
+ imageBuffer.add(currentFrame);
+ if (imageBuffer.size() > imageBufferLenght) {
+ imageBuffer.remove(0);
+ setBufferFull(true);
+ } else {
+ setBufferFull(false);
+ }
+ } else {
+ setBufferFull(true);
+ }
+ //Update data
+ if (!renderer.isPaused()) {
+ updateStreamData();
+ }
+ updateMarker();
+ }
+ }
+ manageFit(bi, data);
+ manageUserOverlays(bi, data);
+ }
+ //updateImageData();
+ }
+
+ @Override
+ public void onError(Object o, Exception ex) {
+ //System.err.println(ex);
+ }
+ });
+
+ } catch (Exception ex) {
+ showException(ex);
+ renderer.clearOverlays();
+ updateServerControls();
+ if (renderer.getDevice() == null) {
+ //renderer.setZoom(1.0);
+ //renderer.setMode(RendererMode.Zoom);
+ errorOverlay = new Text(renderer.getPenErrorText(), ex.toString(), new Font("Verdana", Font.PLAIN, 12), new Point(20, 20));
+ errorOverlay.setFixed(true);
+ errorOverlay.setAnchor(Overlay.ANCHOR_VIEWPORT_TOP_LEFT);
+ renderer.addOverlay(errorOverlay);
+ }
+ } finally {
+ //checkReticle();
+ onTimer();
+ }
+ onChangeColormap(null);
+ checkBackground.setEnabled(true);
+ if (changed) {
+ boolean electrons = getCameraType(cameraName).equals(ELECTRONS_TYPE);
+ comboScreen.setModel(new DefaultComboBoxModel());
+ comboScreen.setEnabled(false);
+ comboFilter.setModel(new DefaultComboBoxModel());
+ comboFilter.setEnabled(false);
+ panelFilter.setVisible(electrons);
+ panelScreen.setVisible(electrons);
+ if (electrons) {
+ //Parallelizing initialization
+ devicesInitTask = new Thread(() -> {
+ try {
+ if (cameraName.contains("DSRM")) {
+ screen = new DiscretePositioner("CurrentScreen", cameraName + ":POSITION_SP", cameraName + ":POSITION");
+ } else {
+ screen = new DiscretePositioner("CurrentScreen", cameraName + ":SET_SCREEN1_POS", cameraName + ":GET_SCREEN1_POS");
+ }
+ screen.setMonitored(true);
+ screen.initialize();
+ DefaultComboBoxModel model = new DefaultComboBoxModel();
+ for (String pos : screen.getPositions()) {
+ model.addElement(pos);
+ }
+ comboScreen.setModel(model);
+ comboScreen.setSelectedItem(screen.read());
+
+ } catch (Exception ex) {
+ comboScreen.setModel(new DefaultComboBoxModel());
+ System.err.println(ex.getMessage());
+ screen = null;
+ }
+ comboScreen.setEnabled(screen != null);
+ valueScreen.setDevice(screen);
+
+ try {
+ filter = new DiscretePositioner("CurrentFilter", cameraName + ":SET_FILTER", cameraName + ":GET_FILTER");
+ filter.setMonitored(true);
+ filter.initialize();
+ DefaultComboBoxModel model = new DefaultComboBoxModel();
+ for (String pos : filter.getPositions()) {
+ model.addElement(pos);
+ }
+ comboFilter.setModel(model);
+ comboFilter.setSelectedItem(filter.read());
+ } catch (Exception ex) {
+ System.err.println(ex.getMessage());
+ filter = null;
+ }
+ comboFilter.setEnabled(filter != null);
+ valueFilter.setDevice(filter);
+ });
+ devicesInitTask.start();
+ }
+ }
+ }
+
+ class ImageIntegrator extends ColormapSource {
+
+ ImageIntegrator(int num) {
+ super("Image Averager", camera.getConfig());
+ boolean continuous = (num < 0);
+ final int numImages = Math.abs(num);
+
+ camera.addListener(new ImageListener() {
+ final ArrayList buffer = new ArrayList();
+ Data integration = null;
+
+ @Override
+ public void onImage(Object o, BufferedImage bi, Data data) {
+ try{
+ if (continuous) {
+ buffer.add(data);
+ if (buffer.size() >= numImages) {
+ for (Data d : buffer) {
+ process(d);
+ }
+ }
+ } else {
+ buffer.add(null); //Just to count
+ process(data);
+ }
+ }catch (Exception ex){
+ buffer.clear();
+ integration = null;
+ ImageIntegrator.this.pushData(null);
+ ex.printStackTrace();
+ return;
+ }
+ if (buffer.size() >= numImages) {
+ if (continuous) {
+ buffer.remove(0);
+ } else {
+ buffer.clear();
+ }
+ if (integration != null) {
+ //integration.div(numImages);
+ ImageIntegrator.this.pushData(integration);
+ }
+ integration = null;
+ }
+ }
+
+ void process(Data data) {
+ if (integration == null) {
+ integration = new Data(data);
+ } else {
+ integration.sum(data);
+ }
+ }
+
+ @Override
+ public void onError(Object origin, Exception ex) {
+ }
+ });
+
+ }
+ }
+
+ boolean bufferFull = true;
+
+ void setBufferFull(boolean value){
+ if (value != bufferFull){
+ SwingUtilities.invokeLater(()->{
+ buttonPause.setBackground(value ? buttonSave.getBackground() : buttonSave.getBackground().brighter());
+ });
+ bufferFull = value;
+ }
+ }
+
+ volatile Dimension imageSize;
+
+ void checkReticle() {
+ if ((renderer.getDevice() != null) && (camera != null) && (camera.getConfig().isCalibrated()) && buttonReticle.isSelected()) {
+ //renderer.setCalibration(camera.getCalibration());
+ renderer.configureReticle(new Dimension(800, 800), 200);
+ renderer.setShowReticle(true);
+ } else {
+ //renderer.setCalibration(null);
+ renderer.setShowReticle(false);
+ }
+ renderer.refresh();
+ }
+
+ void checkMarker(Point p) throws IOException {
+ if (camera != null) {
+ if (buttonMarker.isSelected()) {
+ Dimension d = renderer.getImageSize();
+ if (p==null){
+ p = (d == null) ? new Point(renderer.getWidth() / 2, renderer.getHeight() / 2) : new Point(d.width / 2, d.height / 2);
+ }
+ Overlay ov = null;
+ marker = new Overlays.Crosshairs(renderer.getPenMarker(), p, new Dimension(100, 100));
+ marker.setMovable(true);
+ marker.setPassive(false);
+ } else {
+ marker = null;
+ }
+ renderer.setMarker(marker);
+ onMarkerChanged();
+ }
+ }
+
+ Point lastMarkerPos;
+ void onMarkerChanged() throws IOException {
+ lastMarkerPos = getStreamMarkerPos();
+ if (marker == null) {
+ setInstanceConfigValue("Marker", null);
+ } else {
+ setInstanceConfigValue("Marker", new int[]{marker.getPosition().x, marker.getPosition().y});
+ }
+ }
+
+ void updateMarker() {
+ try {
+ if (server != null) {
+ Point p = getStreamMarkerPos();
+ if (p != null) {
+ //To prevent a local change being overriden by a message having the old settings.
+ //TODO: This is not bullet-proof, as one can have 2 changes between 2 frames...
+ if (!p.equals(lastMarkerPos)){
+ if (p.x == Integer.MIN_VALUE) {
+ if (buttonMarker.isSelected()) {
+ buttonMarker.setSelected(false);
+ checkMarker(null);
+ }
+ } else {
+ if (!buttonMarker.isSelected()) {
+ buttonMarker.setSelected(true);
+ checkMarker(p);
+ } else {
+ if (!p.equals(marker.getPosition())){
+ marker.setPosition(p);
+ }
+ }
+ }
+ }
+ }
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ Point getStreamMarkerPos() throws IOException{
+ //System.out.println(server.getInstanceConfig().get("Marker"));
+ Map pars = server.getProcessingParameters();
+ if (pars != null) {
+ List markerPosition = (List) pars.get("Marker");
+ if (markerPosition != null) {
+ return new Point((Integer)markerPosition.get(0), (Integer)markerPosition.get(1));
+ }
+ return new Point(Integer.MIN_VALUE,Integer.MIN_VALUE);
+ }
+ return null;
+ }
+ void clearMarker(){
+ marker = null;
+ renderer.setMarker(marker);
+ }
+
+ void setInstanceConfigValue(String name, Object value) throws IOException {
+ if (server != null) {
+ Map map = server.getInstanceConfig();
+ map.put(name, value);
+ server.setInstanceConfig(map);
+ }
+ }
+
+ void updateZoom() {
+ try {
+ buttonZoomStretch.setSelected(renderer.getMode() == RendererMode.Stretch);
+ buttonZoomFit.setSelected(renderer.getMode() == RendererMode.Fit);
+ if (renderer.getMode() == RendererMode.Fixed) {
+ buttonZoomNormal.setSelected(true);
+ } else if (renderer.getMode() == RendererMode.Zoom) {
+ if (renderer.getZoom() == 1) {
+ buttonZoomNormal.setSelected(true);
+ } else if (renderer.getZoom() == 0.5) {
+ buttonZoom05.setSelected(true);
+ } else if (renderer.getZoom() == 0.25) {
+ buttonZoom025.setSelected(true);
+ } else if (renderer.getZoom() == 2.0) {
+ buttonZoom2.setSelected(true);
+ } else {
+ buttonGroup1.clearSelection();
+ }
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ boolean updatingColormap;
+
+ void updateColormap() {
+ updatingColormap = true;
+ try {
+ if ((camera != null) && (camera instanceof ColormapSource)) {
+ ColormapSourceConfig config = ((ColormapSource) camera).getConfig();
+ comboColormap.setSelectedItem(config.colormap);
+ if (config.isDefaultColormap()) {
+ buttonFullRange.setSelected(true);
+ } else if (config.colormapAutomatic) {
+ buttonAutomatic.setSelected(true);
+ } else {
+ buttonManual.setSelected(true);
+ }
+ btFixColormapRange.setVisible(buttonAutomatic.isSelected());
+ spinnerMin.setEnabled(buttonManual.isSelected());
+ spinnerMax.setEnabled(buttonManual.isSelected());
+ if (!Double.isNaN(config.colormapMin)) {
+ spinnerMin.setValue(Math.min(Math.max((int) config.colormapMin, 0), 65535));
+ }
+ if (!Double.isNaN(config.colormapMax)) {
+ spinnerMax.setValue(Math.min(Math.max((int) config.colormapMax, 0), 65535));
+ }
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ updatingColormap = false;
+ }
+
+ boolean updatingServerControls;
+
+ void updateServerControls() {
+ if (server != null) {
+ updatingServerControls = true;
+ try {
+ checkBackground.setSelected(server.getBackgroundSubtraction());
+ Double threshold = (server.getThreshold());
+ checkThreshold.setSelected(threshold != null);
+ spinnerThreshold.setValue((threshold == null) ? 0 : threshold);
+ Map gr = (server.getGoodRegion());
+ checkGoodRegion.setSelected(gr != null);
+ if (gr != null) {
+ spinnerGrThreshold.setValue(((Number) gr.get("threshold")).doubleValue());
+ spinnerGrScale.setValue(((Number) gr.get("gfscale")).doubleValue());
+ }
+ Map slicing = (server.getSlicing());
+ checkSlicing.setSelected(slicing != null);
+ if (slicing != null) {
+ spinnerSlNumber.setValue(((Number) slicing.get("number_of_slices")).intValue());
+ spinnerSlScale.setValue(((Number) slicing.get("scale")).doubleValue());
+ spinnerSlOrientation.setValue((String) slicing.get("orientation"));
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ goodRegion = checkGoodRegion.isSelected();
+ spinnerThreshold.setVisible(checkThreshold.isSelected());
+ setGoodRegionOptionsVisible(goodRegion);
+ slicing = goodRegion && checkSlicing.isSelected();
+ setSlicingOptionsVisible(slicing);
+ updatingServerControls = false;
+ }
+ }
+
+ void setGoodRegionOptionsVisible(boolean visible) {
+ spinnerGrThreshold.setVisible(visible);
+ labelGrThreshold.setVisible(visible);
+ spinnerGrScale.setVisible(visible);
+ labelGrScale.setVisible(visible);
+ panelSlicing.setVisible(visible);
+ }
+
+ void setSlicingOptionsVisible(boolean visible) {
+ spinnerSlNumber.setVisible(visible);
+ labelSlNumber.setVisible(visible);
+ spinnerSlScale.setVisible(visible);
+ labelSlScale.setVisible(visible);
+ spinnerSlOrientation.setVisible(visible);
+ labelSlOrientation.setVisible(visible);
+ }
+
+ boolean isCameraStopped() {
+ if ((server != null) && !server.isStarted()) {
+ return true;
+ }
+ return ((camera == null) || camera.isClosed());
+ }
+
+ boolean updatingButtons;
+
+ void updateButtons() {
+ updatingButtons = true;
+ try {
+ boolean active = !isCameraStopped();//(camera != null);
+ buttonSave.setEnabled(active);
+ buttonGrabBackground.setEnabled(active);
+ buttonMarker.setEnabled(active);
+ buttonProfile.setEnabled(active);
+ buttonFit.setEnabled(active);
+ buttonReticle.setEnabled(active && camera.getConfig().isCalibrated());
+ buttonStreamData.setEnabled(active && (server != null));
+ buttonPause.setEnabled(active);
+
+ if (renderer.isPaused() != buttonPause.isSelected()) {
+ buttonPause.setSelected(renderer.isPaused());
+ buttonPauseActionPerformed(null);
+ }
+ if (renderer.getShowReticle() != buttonReticle.isSelected()) {
+ //buttonReticle.setSelected(renderer.getShowReticle());
+ }
+ if ((renderer.getMarker() == null) && buttonMarker.isSelected()) {
+ buttonMarker.setSelected(false);
+ }
+ buttonSave.setSelected(renderer.isSnapshotDialogVisible());
+
+ } finally {
+ updatingButtons = false;
+ }
+ }
+
+ @Override
+ protected void onTimer() {
+ for (Device dev : new Device[]{screen, filter}) {
+ if (dev != null) {
+ dev.request();
+ }
+ }
+
+ textState.setText((camera == null) ? "" : camera.getState().toString());
+ if (App.hasArgument("s")) {
+ try {
+ ((Source) getDevice("image")).initialize();
+ } catch (IOException ex) {
+ Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
+ } catch (InterruptedException ex) {
+ Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ updateZoom();
+ updateColormap();
+ updateButtons();
+ checkHistogram.setSelected((histogramDialog != null) && (histogramDialog.isShowing()));
+ }
+
+ Pen penFit = new Pen(new Color(192, 105, 0), 0);
+ Pen penCross = new Pen(new Color(192, 105, 0), 0);
+ Pen penSlices = new Pen(Color.CYAN.darker(), 1);
+
+ Frame getCurrentFrame() {
+ if ((imageBufferLenght > 1) && (renderer.isPaused())) {
+ int index = ((int) pauseSelection.getValue()) - 1;
+ synchronized (imageBuffer) {
+ return (index < imageBuffer.size()) ? imageBuffer.get(index) : null;
+ }
+ }
+ return currentFrame;
+ }
+
+ Frame getFrame(Data data) {
+ synchronized (imageBuffer) {
+ for (Frame f : imageBuffer) {
+ if (f.data == data) {
+ return f;
+ }
+ }
+ }
+ return null;
+ }
+
+ void setImageBufferSize(int size) {
+ if (renderer.isPaused()) {
+ throw new RuntimeException("Cannot change buffer size whn paused");
+ }
+ synchronized (imageBuffer) {
+ imageBufferLenght = size;
+ imageBuffer.clear();
+ }
+
+ }
+
+ Overlay[][] getFitOverlays(Data data) {
+ Overlays.Polyline hgaussian = null;
+ Overlays.Polyline vgaussian = null;
+ Overlays.Polyline hprofile = null;
+ Overlays.Polyline vprofile = null;
+ Double xMean = null, xSigma = null, xNorm = null, xCom = null, xRms = null;
+ Double yMean = null, ySigma = null, yNorm = null, yCom = null, yRms = null;
+ double[] pX = null, pY = null, gX = null, gY = null;
+ PointDouble[] sliceCenters = null;
+ if (data != null) {
+ int height = data.getHeight();
+ int width = data.getWidth();
+ int profileSize = renderer.getProfileSize();
+ if ((useServerStats) && (server != null)) {
+ try {
+
+ ImageData id = getFrame(data);
+ if (id == null) {
+ return null;
+ }
+ xMean = id.x_fit_mean;
+ xSigma = id.x_fit_standard_deviation;
+ yMean = id.y_fit_mean;
+ ySigma = id.y_fit_standard_deviation;
+ gX = id.x_fit_gauss_function;
+ gY = id.y_fit_gauss_function;
+ pX = id.x_profile;
+ pY = id.y_profile;
+ xCom = id.x_center_of_mass;
+ xRms = id.x_rms;
+ yCom = id.y_center_of_mass;
+ yRms = id.y_rms;
+ sliceCenters = id.sliceCenters;
+
+ profileSize /= 4;
+ if (pX != null) {
+ int[] xp = Arr.indexesInt(pX.length);
+ int[] xg = xp;
+ int[] yp = new int[pX.length];
+ int[] yg = new int[pX.length];
+
+ List l = Arrays.asList((Double[]) Convert.toWrapperArray(pX));
+ double minProfile = Collections.min(l);
+ double maxProfile = Collections.max(l);
+ double rangeProfile = maxProfile - minProfile;
+ double minGauss = minProfile;
+ double rangeGauss = rangeProfile;
+ //If not good region, range of profile and fit are similar so save this calcultion
+ if (goodRegion && id.gr_size_x > 0) {
+ l = Arrays.asList((Double[]) Convert.toWrapperArray(Arrays.copyOfRange(gX, id.gr_pos_x, id.gr_pos_x + id.gr_size_x)));
+ minGauss = Collections.min(l);
+ rangeGauss = Collections.max(l) - minGauss;
+ }
+
+ for (int i = 0; i < xp.length; i++) {
+ if (gX != null) {
+ yg[i] = (int) (height - 1 - (((gX[i] - minGauss) / rangeGauss) * profileSize));
+ }
+ yp[i] = (int) (height - 1 - (((pX[i] - minProfile) / rangeProfile) * profileSize));
+ }
+
+ if (goodRegion && id.gr_size_x > 0) {
+ xg = Arrays.copyOfRange(xg, id.gr_pos_x, id.gr_pos_x + id.gr_size_x);
+ yg = Arrays.copyOfRange(yg, id.gr_pos_x, id.gr_pos_x + id.gr_size_x);
+ }
+
+ vgaussian = new Overlays.Polyline(penFit, xg, yg);
+ vprofile = new Overlays.Polyline(renderer.getPenProfile(), xp, yp);
+ }
+
+ if (pY != null) {
+ int[] xp = new int[pY.length];
+ int[] xg = new int[pY.length];
+ int[] yp = Arr.indexesInt(pY.length);
+ int[] yg = yp;
+
+ List l = Arrays.asList((Double[]) Convert.toWrapperArray(pY));
+ double minProfile = Collections.min(l);
+ double maxProfile = Collections.max(l);
+ double rangeProfile = maxProfile - minProfile;
+ double minGauss = minProfile;
+ double rangeGauss = rangeProfile;
+ //If not good region, range of profile and fit are similar so save this calcultion
+ if (goodRegion && id.gr_size_y > 0) {
+ l = Arrays.asList((Double[]) Convert.toWrapperArray(Arrays.copyOfRange(gY, id.gr_pos_y, id.gr_pos_y + id.gr_size_y)));
+ minGauss = Collections.min(l);
+ rangeGauss = Collections.max(l) - minGauss;
+ }
+
+ for (int i = 0; i < xp.length; i++) {
+ if (gY != null) {
+ xg[i] = (int) (((gY[i] - minGauss) / rangeGauss) * profileSize);
+ }
+ xp[i] = (int) (((pY[i] - minProfile) / rangeProfile) * profileSize);
+ }
+
+ if (goodRegion && id.gr_size_x > 0) {
+ xg = Arrays.copyOfRange(xg, id.gr_pos_y, id.gr_pos_y + id.gr_size_y);
+ yg = Arrays.copyOfRange(yg, id.gr_pos_y, id.gr_pos_y + id.gr_size_y);
+ }
+ hgaussian = new Overlays.Polyline(penFit, xg, yg);
+ hprofile = new Overlays.Polyline(renderer.getPenProfile(), xp, yp);
+ }
+ } catch (Exception ex) {
+ System.err.println(ex.getMessage());
+ return null;
+ }
+ } else {
+ ArrayProperties properties = data.getProperties();
+ double maxPlot = properties.max;
+ double minPlot = properties.min;
+ double rangePlot = maxPlot - minPlot;
+
+ if (rangePlot <= 0) {
+ return null;
+ }
+ if (renderer.getCalibration() != null) {
+ try {
+ double[] sum = data.integrateVertically(true);
+ double[] saux = new double[sum.length];
+ int[] p = new int[sum.length];
+ double[] x_egu = renderer.getCalibration().getAxisX(sum.length);
+ double[] comRms = getComRms(sum, x_egu);
+ xCom = comRms[0];
+ xRms = comRms[1];
+ int[] x = Arr.indexesInt(sum.length);
+ DescriptiveStatistics stats = new DescriptiveStatistics(sum);
+ double min = stats.getMin();
+ for (int i = 0; i < sum.length; i++) {
+ saux[i] = sum[i] - min;
+ }
+ if (showFit) {
+ double[] gaussian = fitGaussian(saux, x);
+ if (gaussian != null) {
+ if ((gaussian[2] < sum.length * 0.45)
+ && (gaussian[2] > 2)
+ && (gaussian[0] > min * 0.03)) {
+ xNorm = gaussian[0];
+ xMean = gaussian[1];
+ xSigma = gaussian[2];
+ double[] fit = getFitFunction(gaussian, x);
+ int[] y = new int[x.length];
+ for (int i = 0; i < x.length; i++) {
+ y[i] = (int) (height - 1 - ((((fit[i] + min) / height - minPlot) / rangePlot) * profileSize));
+ }
+ vgaussian = new Overlays.Polyline(penFit, x, y);
+ }
+ }
+ }
+ if (showProfile) {
+ for (int i = 0; i < x.length; i++) {
+ p[i] = (int) (height - 1 - (((sum[i] / height - minPlot) / rangePlot) * profileSize));
+ }
+ vprofile = new Overlays.Polyline(renderer.getPenProfile(), x, p);
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+
+ try {
+ double[] sum = data.integrateHorizontally(true);
+ double[] saux = new double[sum.length];
+ int[] p = new int[sum.length];
+ double[] y_egu = renderer.getCalibration().getAxisY(sum.length);
+ double[] comRms = getComRms(sum, y_egu);
+ yCom = comRms[0];
+ yRms = comRms[1];
+ int[] x = Arr.indexesInt(sum.length);
+ DescriptiveStatistics stats = new DescriptiveStatistics(sum);
+ double min = stats.getMin();
+ for (int i = 0; i < sum.length; i++) {
+ saux[i] = sum[i] - min;
+ }
+
+ if (showFit) {
+ double[] gaussian = fitGaussian(saux, x);
+ if (gaussian != null) {
+ //Only aknowledge beam fully inside the image and peak over 3% of min
+ if ((gaussian[2] < sum.length * 0.45)
+ && (gaussian[2] > 2)
+ && (gaussian[0] > min * 0.03)) {
+ yNorm = gaussian[0];
+ yMean = gaussian[1];
+ ySigma = gaussian[2];
+ double[] fit = getFitFunction(gaussian, x);
+
+ int[] y = new int[x.length];
+ for (int i = 0; i < x.length; i++) {
+ y[i] = (int) ((((fit[i] + min) / width - minPlot) / rangePlot) * profileSize);
+ }
+ hgaussian = new Overlays.Polyline(penFit, y, x);
+ }
+ }
+ }
+ if (showProfile) {
+ for (int i = 0; i < x.length; i++) {
+ p[i] = (int) (((sum[i] / width - minPlot) / rangePlot) * profileSize);
+ }
+ hprofile = new Overlays.Polyline(renderer.getPenProfile(), p, x);
+ }
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ if (xSigma != null) {
+ xSigma *= renderer.getCalibration().getScaleX();
+ }
+ if (ySigma != null) {
+ ySigma *= renderer.getCalibration().getScaleY();
+ }
+ if (xMean != null) {
+ xMean = data.getX((int) Math.round(xMean));
+ }
+ if (yMean != null) {
+ yMean = data.getY((int) Math.round(yMean));
+ }
+ }
+ }
+ final String units = (renderer.getCalibration() != null) ? "\u00B5m" : "px";
+ final String fmt = "%7.1f" + units;
+ Overlays.Text textCom = null;
+ Overlay[] pOv = null, fOv = null;
+ Font fontInfoText = new Font(Font.MONOSPACED, 0, 14);
+ Point textPosition = new Point(12, 20);
+ if (showProfile) {
+ if ((xCom != null) && (yCom != null)) {
+ String text = String.format("com x: m=" + fmt + " \u03C3=" + fmt + "\ncom y: m=" + fmt + " \u03C3=" + fmt, xCom, xRms, yCom, yRms);
+ textCom = new Overlays.Text(renderer.getPenProfile(), text, fontInfoText, textPosition);
+ textCom.setFixed(true);
+ textCom.setAnchor(Overlay.ANCHOR_VIEWPORT_TOP_LEFT);
+ }
+ pOv = new Overlay[]{hprofile, vprofile, textCom};
+ textPosition = new Point(textPosition.x, textPosition.y + 34);
+ }
+ if (showFit) {
+ Overlays.Crosshairs cross = null;
+ Overlays.Text textFit = null;
+ if ((xMean != null) && (yMean != null)) {
+ String text = String.format("fit x: m=" + fmt + " \u03C3=" + fmt + "\nfit y: m=" + fmt + " \u03C3=" + fmt, xMean, xSigma, yMean, ySigma);
+ textFit = new Overlays.Text(penFit, text, fontInfoText, textPosition);
+ textFit.setFixed(true);
+ textFit.setAnchor(Overlay.ANCHOR_VIEWPORT_TOP_LEFT);
+ Point center = new Point(xMean.intValue(), yMean.intValue());
+ if (renderer.getCalibration() != null) {
+ center = renderer.getCalibration().convertToImagePosition(new PointDouble(xMean, yMean));
+ xSigma /= renderer.getCalibration().getScaleX();
+ ySigma /= renderer.getCalibration().getScaleY();
+ }
+ cross = new Overlays.Crosshairs(penCross, center, new Dimension(Math.abs(2 * xSigma.intValue()), 2 * Math.abs(ySigma.intValue())));
+ }
+ textPosition = new Point(textPosition.x, textPosition.y + 34);
+ fOv = new Overlay[]{hgaussian, vgaussian, cross, textFit};
+
+ if (goodRegion) {
+ try {
+ double[] x = getServerDoubleArray("gr_x_axis");
+ double[] y = getServerDoubleArray("gr_y_axis");
+ double x1 = x[0];
+ double x2 = x[x.length - 1];
+ double y1 = y[0];
+ double y2 = y[y.length - 1];
+ Overlays.Rect goodRegionOv = new Overlays.Rect(new Pen(penFit.getColor(), 0, Pen.LineStyle.dotted));
+ goodRegionOv.setCalibration(renderer.getCalibration());
+ goodRegionOv.setAbsolutePosition(new PointDouble(x1, y1));
+ goodRegionOv.setAbsoluteSize(new DimensionDouble(x2 - x1, y2 - y1));
+ fOv = Arr.append(fOv, goodRegionOv);
+
+ if (slicing) {
+ if (sliceCenters != null) {
+ for (PointDouble sliceCenter : sliceCenters) {
+ Overlays.Crosshairs center = new Overlays.Crosshairs(penSlices);
+ center.setCalibration(renderer.getCalibration());
+ center.setAbsolutePosition(sliceCenter);
+ center.setSize(new Dimension(10, 10));
+ fOv = Arr.append(fOv, center);
+ }
+ if (sliceCenters.length > 1) {
+ double[] fit = fitPolynomial(sliceCenters, 1);
+ double angle = Math.toDegrees(Math.atan(fit[1]));
+ Overlays.Text text = new Overlays.Text(penSlices, String.format("slice: \u03B8= %5.1fdeg", angle), fontInfoText, textPosition);
+ text.setFixed(true);
+ text.setAnchor(Overlay.ANCHOR_VIEWPORT_TOP_LEFT);
+ fOv = Arr.append(fOv, text);
+ }
+ }
+ }
+ } catch (Exception ex) {
+ }
+ }
+
+ }
+ return new Overlay[][]{pOv, fOv};
+ }
+ return null;
+ }
+
+ class UserOverlay {
+
+ String name;
+ Overlay obj;
+ String[] channels;
+ }
+ ArrayList userOverlayConfig;
+
+ void parseUserOverlays() {
+ Properties userOverlays = new Properties();
+ userOverlayConfig = new ArrayList<>();
+ if (userOverlaysConfigFile != null) {
+ try {
+ try (FileInputStream in = new FileInputStream(getContext().getSetup().expandPath(userOverlaysConfigFile))) {
+ userOverlays.load(in);
+
+ for (String name : userOverlays.stringPropertyNames()) {
+ String val = userOverlays.getProperty(name);
+ try {
+ UserOverlay uo = new UserOverlay();
+ uo.name = name;
+ String type = val.substring(0, val.indexOf("(")).trim();
+ String pars = val.substring(val.indexOf("(") + 1, val.lastIndexOf(")")).trim();
+ String[] tokens = pars.split(",");
+ for (int i = 0; i < tokens.length; i++) {
+ tokens[i] = tokens[i].trim();
+ }
+ Color color = Color.GRAY;
+ try {
+ color = (Color) Color.class.getField(tokens[tokens.length - 1].toUpperCase()).get(null);
+ } catch (Exception ex) {
+ }
+ Pen pen = new Pen(color);
+ try {
+ String[] penTokens = tokens[tokens.length - 1].split(":");
+ color = (Color) Color.class.getField(penTokens[0].toUpperCase()).get(null);
+ int width = Integer.valueOf(penTokens[1]);
+ Pen.LineStyle style = Pen.LineStyle.valueOf(penTokens[2]);
+ pen = new Pen(color, width, style);
+ } catch (Exception ex) {
+ }
+ switch (type) {
+ case "Point":
+ uo.obj = new Overlays.Crosshairs();
+ uo.obj.setSize(new Dimension(Integer.valueOf(tokens[2]), Integer.valueOf(tokens[3])));
+ break;
+ case "Line":
+ uo.obj = new Overlays.Line();
+ break;
+ case "Arrow":
+ uo.obj = new Overlays.Arrow();
+ break;
+ case "Rect":
+ uo.obj = new Overlays.Rect();
+ break;
+ case "Ellipse":
+ uo.obj = new Overlays.Ellipse();
+ break;
+ case "Polyline":
+ uo.obj = new Overlays.Polyline();
+ break;
+ }
+ if (type.equals("Polyline") || type.equals("Point")) {
+ uo.channels = new String[]{tokens[0], tokens[1]};
+ } else {
+ uo.channels = new String[]{tokens[0], tokens[1], tokens[2], tokens[3]};
+ }
+ uo.obj.setPen(pen);
+ userOverlayConfig.add(uo);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ Overlay[] getUserOverlays(Data data) {
+ ArrayList ret = new ArrayList<>();
+ if (server != null) {
+ for (UserOverlay uo : userOverlayConfig) {
+ try {
+ Overlay ov = uo.obj;
+ //Overlay ov = (Overlay)uo.cls.newInstance();
+ ov.setCalibration(renderer.getCalibration());
+ boolean valid = false;
+ if (ov instanceof Overlays.Polyline) {
+ double[] x = (uo.channels[0].equals("null")) ? null : getServerDoubleArray(uo.channels[0]);
+ double[] y = (uo.channels[1].equals("null")) ? null : getServerDoubleArray(uo.channels[1]);
+ if ((x != null) || (y != null)) {
+ if (x == null) {
+ x = (renderer.getCalibration() == null) ? Arr.indexesDouble(y.length) : renderer.getCalibration().getAxisX(y.length);
+ }
+ if (y == null) {
+ y = (renderer.getCalibration() == null) ? Arr.indexesDouble(x.length) : renderer.getCalibration().getAxisY(x.length);
+ }
+ ((Overlays.Polyline) ov).updateAbsolute(x, y);
+ valid = true;
+ }
+ } else {
+ Double x = getServerDouble(uo.channels[0]);
+ Double y = getServerDouble(uo.channels[1]);
+ if ((x != null) && (y != null)) {
+ PointDouble position = new PointDouble(x, y);
+ ov.setAbsolutePosition(position);
+ if (!(ov instanceof Overlays.Crosshairs)) {
+ Double x2 = getServerDouble(uo.channels[2]);
+ Double y2 = getServerDouble(uo.channels[3]);
+ if ((x != null) && (y != null)) {
+ DimensionDouble size = new DimensionDouble(x2 - position.x, y2 - position.y);
+ ov.setAbsoluteSize(size);
+ valid = true;
+ }
+ } else {
+ valid = true;
+ }
+ }
+ }
+ if (valid) {
+ ret.add(ov);
+ }
+ } catch (Exception ex) {
+ //ex.printStackTrace();
+ }
+ }
+ }
+ return ret.toArray(new Overlay[0]);
+ }
+
+ double[] getComRms(double[] arr, double[] x) {
+ if (arr != null) {
+ double xmd = 0;
+ double xmd2 = 0;
+ double total = 0;
+ for (int i = 0; i < arr.length; i++) {
+ double v = (arr[i] * x[i]);
+ xmd += v;
+ xmd2 += (v * x[i]);
+ total += arr[i];
+ }
+ if (total > 0) {
+ double com = xmd / total;
+ double com2 = xmd2 / total;
+ double rms = Math.sqrt(Math.abs(com2 - com * com));
+ return new double[]{com, rms};
+ }
+ }
+ return new double[]{Double.NaN, Double.NaN};
+ }
+
+ double[] fitGaussianScript(int[] y, int[] x) {
+ ScriptManager sm = Context.getInstance().getScriptManager();
+ ArrayProperties pY = ArrayProperties.get(y);
+ sm.setVar("y", y);
+ sm.setVar("x", x);
+ InterpreterResult r = sm.eval("r = fit_gaussians(y, x, [" + pY.maxIndex + ",])");
+ if (r.exception != null) {
+ r.exception.printStackTrace();
+ } else {
+ List ret = (List) sm.getVar("r");
+ if ((ret != null) && (ret.size() == 1) && (ret.get(0) instanceof List) && (((List) (ret.get(0))).size() == 3)) {
+ double norm = (Double) ((List) ret.get(0)).get(0);
+ double mean = (Double) ((List) ret.get(0)).get(1);
+ double sigma = (Double) ((List) ret.get(0)).get(2);
+ return new double[]{norm, mean, sigma};
+ }
+ }
+ return null;
+ }
+
+ double[] fitGaussian(double[] y, int[] x) {
+ try {
+ ArrayProperties pY = ArrayProperties.get(y);
+ GaussianCurveFitter fitter = GaussianCurveFitter.create().withStartPoint(new double[]{(pY.max - pY.min) / 2, x[pY.maxIndex], 1.0}).withMaxIterations(1000);
+ ArrayList values = new ArrayList<>();
+ for (int i = 0; i < y.length; i++) {
+ values.add(new WeightedObservedPoint(1.0, x[i], y[i]));
+ }
+ return fitter.fit(values);
+ } catch (Exception ex) {
+ return null;
+ }
+
+ }
+
+ double[] fitPolynomial(PointDouble[] points, int order) {
+ double[] y = new double[points.length];
+ double[] x = new double[points.length];
+ for (int i = 0; i < points.length; i++) {
+ x[i] = points[i].x;
+ y[i] = points[i].y;
+ }
+ return fitPolynomial(y, x, order);
+ }
+
+ double[] fitPolynomial(double[] y, double[] x, int order) {
+ try {
+ ArrayProperties pY = ArrayProperties.get(y);
+ PolynomialCurveFitter fitter = PolynomialCurveFitter.create(order).withMaxIterations(1000);
+ ArrayList values = new ArrayList<>();
+ for (int i = 0; i < y.length; i++) {
+ values.add(new WeightedObservedPoint(1.0, x[i], y[i]));
+ }
+ return fitter.fit(values);
+ } catch (Exception ex) {
+ return null;
+ }
+
+ }
+
+ double[] getFitFunction(double[] pars, int[] x) {
+ double[] fit = new double[x.length];
+ Gaussian g = new Gaussian(pars[0], pars[1], pars[2]);
+ for (int i = 0; i < x.length; i++) {
+ fit[i] = g.value(x[i]);
+ }
+ return fit;
+ }
+
+ void setHistogramVisible(boolean value) {
+ if (value) {
+ if ((histogramDialog == null) || (!histogramDialog.isShowing())) {
+ Histogram histogram = new Histogram(true);
+ histogram.setRenderer(renderer);
+ histogramDialog = SwingUtils.showDialog(SwingUtils.getWindow(renderer), "Histogram", null, histogram);
+ renderer.refresh();
+ }
+ } else {
+ if (histogramDialog != null) {
+ histogramDialog.setVisible(false);
+ histogramDialog = null;
+ }
+ }
+ }
+
+ void setLaserState(boolean value) throws Exception {
+ System.out.println("Setting laser state: " + value);
+ Epics.putq("SIN-TIMAST-TMA:Beam-Las-Delay-Sel", value ? 0 : 1);
+ Epics.putq("SIN-TIMAST-TMA:Beam-Apply-Cmd.PROC", 1);
+ Thread.sleep(3000);
+ }
+
+ boolean getLaserState() throws Exception {
+ return (Epics.get("SIN-TIMAST-TMA:Beam-Las-Delay-Sel", Integer.class) == 0);
+ }
+
+ void elog(String logbook, String title, String message, String[] attachments) throws Exception {
+ String domain = "";
+ String category = "Info";
+ String entry = "";
+ StringBuffer cmd = new StringBuffer();
+
+ cmd.append("G_CS_ELOG_add -l \"").append(logbook).append("\" ");
+ cmd.append("-a \"Author=ScreenPanel\" ");
+ cmd.append("-a \"Type=pshell\" ");
+ cmd.append("-a \"Entry=").append(entry).append("\" ");
+ cmd.append("-a \"Title=").append(title).append("\" ");
+ cmd.append("-a \"Category=").append(category).append("\" ");
+ cmd.append("-a \"Domain=").append(domain).append("\" ");
+ for (String attachment : attachments) {
+ cmd.append("-f \"").append(attachment).append("\" ");
+ }
+ cmd.append("-n 1 ");
+ cmd.append("\"").append(message).append("\" ");
+ System.out.println(cmd.toString());
+
+ final Process process = Runtime.getRuntime().exec(new String[]{"bash", "-c", cmd.toString()});
+ new Thread(() -> {
+ try {
+ process.waitFor();
+ int bytes = process.getInputStream().available();
+ byte[] arr = new byte[bytes];
+ process.getInputStream().read(arr, 0, bytes);
+ System.out.println(new String(arr));
+ bytes = process.getErrorStream().available();
+ arr = new byte[bytes];
+ process.getErrorStream().read(arr, 0, bytes);
+ System.err.println(new String(arr));
+ } catch (Exception ex) {
+ System.err.println(ex);
+ }
+ }).start();
+ }
+
+ void centralizeRenderer() {
+ Point center = null;
+ Dimension size = renderer.getImageSize();
+ double zoom = (renderer.getMode() == RendererMode.Fixed) ? 1.0 : renderer.getZoom();
+ if (renderer.getCalibration() != null) {
+ center = renderer.getCalibration().getCenter();
+ } else if (size != null) {
+ center = new Point(size.width / 2, size.height / 2);
+ }
+ if (center != null) {
+ Point topleft = new Point(Math.max((int) (center.x - renderer.getWidth() / 2 / zoom), 0),
+ Math.max((int) (center.y - renderer.getHeight() / 2 / zoom), 0));
+ renderer.setViewPosition(topleft);
+ }
+ }
+
+ void updatePause() {
+ int index = ((int) pauseSelection.getValue()) - 1;
+ synchronized (imageBuffer) {
+ if (index < imageBuffer.size()) {
+ Data data = imageBuffer.get(index).data;
+ long pid = imageBuffer.get(index).cache.getPulseId();
+ BufferedImage image = camera.generateImage(data);
+ renderer.setImage(renderer.getOrigin(), image, data);
+
+ String text = "PID: " + pid;
+ if (imagePauseOverlay == null) {
+ Font font = new Font("Verdana", Font.PLAIN, 12);
+ Dimension d = SwingUtils.getTextSize(text, renderer.getFontMetrics(font));
+ imagePauseOverlay = new Text(renderer.getPenErrorText(), "", font, new Point(-20 - d.width, 42));
+ imagePauseOverlay.setFixed(true);
+ imagePauseOverlay.setAnchor(Overlay.ANCHOR_VIEWPORT_OR_IMAGE_TOP_RIGHT);
+ renderer.addOverlay(imagePauseOverlay);
+ }
+ //imagePauseOverlay.update(Chrono.getTimeStr(data.getTimestamp(), "HH:mm:ss.SSS"));
+ imagePauseOverlay.update(text);
+ manageFit(image, data);
+ manageUserOverlays(image, data);
+ }
+ }
+ updateStreamData();
+ }
+
+ void removePauseOverlay() {
+ renderer.removeOverlay(imagePauseOverlay);
+ imagePauseOverlay = null;
+ }
+
+ void saveSnapshot() throws Exception {
+ String snapshotFile = null;
+ synchronized (imageBuffer) {
+ Frame frame = getCurrentFrame();
+ if (frame == null) {
+ throw new Exception("No current image");
+ }
+ ArrayList frames = new ArrayList<>();
+ frames.add(frame);
+ this.saveFrames("camera_snapshot", frames);
+
+ //Enforce the same timestamp to data & image files.
+ snapshotFile = getContext().getExecutionPars().getPath() + ".png";
+ ImageBuffer.saveImage(SwingUtils.createImage(renderer), snapshotFile, "png");
+ }
+
+ JPanel panel = new JPanel();
+ GridBagLayout layout = new GridBagLayout();
+ layout.columnWidths = new int[]{0, 180}; //Minimum width
+ layout.rowHeights = new int[]{30, 30, 30}; //Minimum height
+ panel.setLayout(layout);
+ JComboBox comboLogbook = new JComboBox(new String[]{"SwissFEL commissioning data", "SwissFEL commissioning"});
+ JTextField textComment = new JTextField();
+ GridBagConstraints c = new GridBagConstraints();
+ c.gridx = 0;
+ c.gridy = 0;
+ panel.add(new JLabel("Data file:"), c);
+ c.gridy = 1;
+ panel.add(new JLabel("Logbook:"), c);
+ c.gridy = 2;
+ panel.add(new JLabel("Comment:"), c);
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridx = 1;
+ panel.add(textComment, c);
+ c.gridy = 1;
+ panel.add(comboLogbook, c);
+ c.gridy = 0;
+ panel.add(new JLabel(getContext().getExecutionPars().getPath()), c);
+
+ if (SwingUtils.showOption(getTopLevel(), "Success", panel, OptionType.OkCancel) == OptionResult.Yes) {
+ StringBuilder message = new StringBuilder();
+ message.append("Camera: ").append(cameraName).append(" (").
+ append((server != null) ? "server" : "direct").append(")").append("\n");
+ message.append("Screen: ").append(String.valueOf(valueScreen.getLabel().getText())).append("\n");
+ message.append("Filter: ").append(String.valueOf(valueFilter.getLabel().getText())).append("\n");
+ message.append("Data file: ").append(getContext().getExecutionPars().getPath()).append("\n");
+ message.append("Comment: ").append(textComment.getText()).append("\n");
+ if ((fitOv != null) && (fitOv.length > 5)) {
+ Overlays.Text text = (Overlays.Text) fitOv[5];
+ message.append(text.getText()).append("\n");
+ }
+ elog((String) comboLogbook.getSelectedItem(), "ScreenPanel Snapshot", message.toString(), new String[]{snapshotFile});
+ }
+ }
+
+ void saveStack() throws Exception {
+ synchronized (imageBuffer) {
+ saveFrames("camera_stack", imageBuffer);
+ }
+ SwingUtils.showMessage(getTopLevel(), "Success", "Generated data file:\n" + getContext().getExecutionPars().getPath(), 5000);
+ }
+
+ public static String getCameraType(String name) {
+ if (name == null) {
+ return "";
+ }
+ for (String s : new String[]{"LCAM"}) {
+ if (name.contains(s)) {
+ return LASER_TYPE;
+ }
+ }
+ for (String s : new String[]{"DSCR", "DSRM", "DLAC"}) {
+ if (name.contains(s)) {
+ return ELECTRONS_TYPE;
+ }
+ }
+ for (String s : new String[]{"PROF", "PPRM", "PSSS", "PSCR", "PSRD"}) {
+ if (name.contains(s)) {
+ return PHOTONICS_TYPE;
+ }
+ }
+ return "Unknown";
+ }
+
+ public Map getProcessingParameters(StreamValue value) throws IOException {
+ return (Map) JsonSerializer.decode(value.getValue("processing_parameters").toString(), Map.class);
+ }
+
+ void saveFrames(String name, ArrayList frames) throws IOException {
+ ArrayList values = new ArrayList<>();
+ for (Frame frame : frames) {
+ values.add(frame.cache);
+ }
+ saveImages(name, values);
+ }
+
+ void saveImages(String name, ArrayList images) throws IOException {
+ int depth = images.size();
+ if (depth == 0) {
+ return;
+ }
+ StreamValue first = images.get(0);
+ String pathRoot = "/camera1/";
+ String pathImage = pathRoot + "image";
+ String pathPid = pathRoot + "pulse_id";
+ String pathTimestampStr = pathRoot + "timestamp_str";
+ Map processingPars = getProcessingParameters(first);
+ String camera = (String) processingPars.get("camera_name");
+ String type = getCameraType(camera);
+
+ int width = ((Number) first.getValue("width")).intValue();
+ int height = ((Number) first.getValue("height")).intValue();
+ Class dataType = first.getValue("image").getClass().getComponentType();
+
+ getContext().setExecutionPars(name);
+ DataManager dm = getContext().getDataManager();
+
+ //Create tables
+ dm.createDataset(pathImage, dataType, new int[]{depth, height, width});
+ dm.createDataset(pathPid, Long.class, new int[]{depth});
+ dm.createDataset(pathTimestampStr, String.class, new int[]{depth});
+ for (String id : first.getIdentifiers()) {
+ Object val = first.getValue(id);
+ if (id.equals("image")) {
+ } else if (id.equals("processing_parameters")) {
+ Map pars = getProcessingParameters(first);
+ for (String key : pars.keySet()) {
+ if ((pars.get(key) != null) && (pars.get(key) instanceof Map)) {
+ for (Object k : ((Map) pars.get(key)).keySet()) {
+ Object v = ((Map) pars.get(key)).get(k);
+ dm.setAttribute(pathImage, key + " " + k, (v == null) ? "" : v);
+ }
+ } else {
+ Object value = pars.get(key);
+ if (value == null) {
+ value = "";
+ } else if (value instanceof List) {
+ Class cls = (((List) value).size() > 0) ? ((List) value).get(0).getClass() : double.class;
+ value = Convert.toPrimitiveArray(value, cls);
+ //value = Convert.toDouble(value);
+ }
+ dm.setAttribute(pathImage, key, value);
+ }
+ }
+ } else if (val.getClass().isArray()) {
+ dm.createDataset(pathRoot + id, Double.class, new int[]{depth, Array.getLength(val)});
+ } else {
+ dm.createDataset(pathRoot + id, val.getClass(), new int[]{depth});
+ }
+ }
+
+ //Add metadata
+ dm.setAttribute(pathRoot, "Camera", camera);
+ dm.setAttribute(pathRoot, "Images", depth);
+ dm.setAttribute(pathRoot, "Interval", -1);
+ dm.setAttribute(pathRoot, "Type", type);
+ if (type.equals(ELECTRONS_TYPE)) {
+ dm.setAttribute(pathRoot, "Screen", String.valueOf(valueScreen.getLabel().getText()));
+ dm.setAttribute(pathRoot, "Filter", String.valueOf(valueFilter.getLabel().getText()));
+ }
+
+ //Save data
+ for (int index = 0; index < depth; index++) {
+ StreamValue streamValue = images.get(index);
+ dm.setItem(pathImage, streamValue.getValue("image"), new long[]{index, 0, 0}, new int[]{1, height, width});
+ dm.setItem(pathPid, streamValue.getPulseId(), index);
+ dm.setItem(pathTimestampStr, Chrono.getTimeStr(streamValue.getTimestamp(), "YYYY-MM-dd HH:mm:ss.SSS"), index);
+
+ for (String id : streamValue.getIdentifiers()) {
+ Object val = streamValue.getValue(id);
+ if (id.equals("image")) {
+ } else if (id.equals("processing_parameters")) {
+ } else if (val.getClass().isArray()) {
+ dm.setItem(pathRoot + id, val, index);
+ } else {
+ dm.setItem(pathRoot + id, val, index);
+ }
+ }
+ }
+ getContext().getDataManager().closeOutput();
+ }
+
+ StandardDialog calibrationDialolg;
+
+ void calibrate() throws Exception {
+ if (server != null) {
+ server.resetRoi();
+ calibrationDialolg = (StandardDialog) getContext().getClassByName("CameraCalibrationDialog").getConstructors()[0].newInstance(new Object[]{getTopLevel(), server.getCurrentCamera(), renderer});
+ SwingUtils.centerComponent(getTopLevel(), calibrationDialolg);
+ calibrationDialolg.setVisible(true);
+ calibrationDialolg.setListener(new StandardDialogListener() {
+ @Override
+ public void onWindowOpened(StandardDialog dlg) {
+ }
+
+ @Override
+ public void onWindowClosed(StandardDialog dlg, boolean accepted) {
+ if (accepted) {
+ //comboCamerasActionPerformed(null);
+ }
+ }
+ });
+ }
+ }
+
+ StandardDialog dataTableDialog;
+ DefaultTableModel dataTableModel;
+ JTable dataTable;
+
+ void showStreamData() {
+ dataTableModel = null;
+ if (server != null) {
+
+ if ((dataTableDialog != null) && (dataTableDialog.isShowing())) {
+ SwingUtils.centerComponent(getTopLevel(), dataTableDialog);
+ dataTableDialog.requestFocus();
+ return;
+ }
+ dataTableModel = new DefaultTableModel(new Object[0][2], new String[]{"Name", "Value"}) {
+ public Class getColumnClass(int columnIndex) {
+ return String.class;
+ }
+
+ public boolean isCellEditable(int rowIndex, int columnIndex) {
+ return false;
+ }
+ };
+ updateStreamData();
+ StreamValue val = server.getStream().take();
+ dataTable = new JTable(dataTableModel);
+ dataTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
+ dataTable.setCellSelectionEnabled(true);
+ dataTable.getTableHeader().setReorderingAllowed(false);
+ dataTable.getTableHeader().setResizingAllowed(true);
+ dataTableDialog = new StandardDialog(getTopLevel(), "Image Data", false);
+ dataTableDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+ JScrollPane scrollPane = new JScrollPane();
+ scrollPane.setViewportView(dataTable);
+ scrollPane.setPreferredSize(new Dimension(300, 400));
+ dataTableDialog.setContentPane(scrollPane);
+ dataTableDialog.pack();
+ dataTableDialog.setVisible(true);
+ dataTableDialog.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosing(WindowEvent e) {
+ dataTableModel = null;
+ }
+ });
+ dataTable.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ try {
+ int row = dataTable.getSelectedRow();
+ int col = dataTable.getSelectedColumn();
+ dataTable.setToolTipText(null);
+ if (row > 1) {
+ String id = String.valueOf(dataTable.getModel().getValueAt(row, 0));
+ String locator = String.valueOf(dataTable.getModel().getValueAt(0, 1));
+ String channelId = locator + " " + id;
+ dataTable.setToolTipText(channelId);
+ if ((e.getClickCount() == 2) && (!e.isPopupTrigger())) {
+ if (col == 0) {
+ SwingUtils.showMessage(dataTableDialog, "Channel Identifier", "Copied to clipboard: " + channelId);
+ Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+ clipboard.setContents(new StringSelection(channelId), (Clipboard clipboard1, Transferable contents) -> {
+ });
+ } else {
+ Object obj = getCurrentFrame().cache.getValue(id);
+ if (id.equals("image")) {
+ } else if (id.equals("processing_parameters")) {
+ Map pars = getProcessingParameters(getCurrentFrame().cache);
+ StringBuilder sb = new StringBuilder();
+ for (String key : pars.keySet()) {
+ sb.append(key).append(" = ").append(Str.toString(pars.get(key), 10)).append("\n");
+ }
+ SwingUtils.showMessage(dataTableDialog, "Processing Parameters", sb.toString());
+ } else if ((obj != null) && (obj.getClass().isArray() || (obj instanceof Number))) {
+ DeviceValueChart chart = new DeviceValueChart();
+ Device dev = null;
+ if (obj.getClass().isArray()) {
+ dev = new ReadableRegisterArray(new ReadableArray() {
+ @Override
+ public Object read() throws IOException, InterruptedException {
+ return Convert.toDouble(getCurrentFrame().cache.getValue(id));
+ }
+
+ @Override
+ public int getSize() {
+ return Array.getLength(getCurrentFrame().cache.getValue(id));
+ }
+ });
+ } else {
+ dev = new ReadableRegisterNumber(new ReadableNumber() {
+ @Override
+ public Object read() throws IOException, InterruptedException {
+ return Convert.toDouble(getCurrentFrame().cache.getValue(id));
+ }
+ });
+ }
+ dev.setPolling(1000);
+ chart.setDevice(dev);
+ JDialog dlg = SwingUtils.showDialog(dataTableDialog, cameraName + " " + id, null, chart);
+ }
+ }
+ }
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }
+ });
+ SwingUtils.centerComponent(getTopLevel(), dataTableDialog);
+ updateStreamData();
+ }
+ }
+
+ volatile boolean updatingStreamData = false;
+
+ void updateStreamData() {
+ if ((dataTableDialog == null) || !dataTableDialog.isShowing() || updatingStreamData) {
+ return;
+ }
+ updatingStreamData = true;
+ SwingUtilities.invokeLater(() -> {
+ updatingStreamData = false;
+ if ((dataTableModel != null) && (server != null)) {
+ StreamValue value = server.getValue();
+ Frame frame = getCurrentFrame();
+ int[] sel_rows = (dataTable == null) ? null : dataTable.getSelectedRows();
+ int[] sel_cols = (dataTable == null) ? null : dataTable.getSelectedColumns();
+ List ids = (value == null) ? new ArrayList<>() : new ArrayList(value.getIdentifiers());
+ if (ids.size() + 4 != dataTableModel.getRowCount()) {
+ dataTableModel.setNumRows(0);
+ try {
+ dataTableModel.addRow(new Object[]{"Locator", server.getUrl() + "/" + ((value == null) ? instanceName : server.getCurrentInstance())});
+ } catch (Exception ex) {
+ dataTableModel.addRow(new Object[]{"Locator", ex.getMessage()});
+ }
+ try {
+ dataTableModel.addRow(new Object[]{"Stream", server.getStreamAddress()});
+ } catch (Exception ex) {
+ dataTableModel.addRow(new Object[]{"Stream", ex.getMessage()});
+ }
+ dataTableModel.addRow(new Object[]{"PID", ""});
+ dataTableModel.addRow(new Object[]{"Timestamp", ""});
+ Collections.sort(ids);
+ for (String id : ids) {
+ dataTableModel.addRow(new Object[]{id, ""});
+ }
+ }
+
+ if ((frame != null) && (frame.cache != null)) {
+ dataTableModel.setValueAt(frame.cache.getPulseId(), 2, 1); //PID
+ dataTableModel.setValueAt(frame.cache.getTimestamp(), 3, 1); //Timestamp
+ for (int i = 4; i < dataTableModel.getRowCount(); i++) {
+ String id = String.valueOf(dataTableModel.getValueAt(i, 0));
+ //Object obj = server.getValue(id);
+ Object obj = frame.cache.getValue(id);
+ if (obj != null) {
+ if (obj.getClass().isArray()) {
+ obj = obj.getClass().getComponentType().getSimpleName() + "[" + Array.getLength(obj) + "]";
+ } else if (obj instanceof Double) {
+ obj = Convert.roundDouble((Double) obj, 1);
+ } else if (obj instanceof Float) {
+ obj = Convert.roundDouble((Float) obj, 1);
+ }
+ }
+ dataTableModel.setValueAt(String.valueOf(obj), i, 1);
+ }
+ }
+ if ((sel_rows != null) && (sel_rows.length > 0)) {
+ //dataTable.setRowSelectionInterval((Integer)Arr.getMin(sel_rows), (Integer)Arr.getMax(sel_rows));
+ dataTable.setRowSelectionInterval(sel_rows[0], sel_rows[sel_rows.length - 1]);
+ }
+ if ((sel_cols != null) && (sel_cols.length > 0)) {
+ //dataTable.setColumnSelectionInterval((Integer)Arr.getMin(sel_cols), (Integer)Arr.getMax(sel_cols));
+ dataTable.setColumnSelectionInterval(sel_cols[0], sel_cols[sel_cols.length - 1]);
+ }
+ }
+ });
+ }
+
+ ImageIcon getIcon(String name) {
+ ImageIcon ret = null;
+ try {
+ //Path path = Paths.get(getClass().getProtectionDomain().getCodeSource().getLocation().getPath(),"resources", name + ".png");
+ String dir = getClass().getProtectionDomain().getCodeSource().getLocation().getPath() + "resources/";
+ if (new File(dir + name + ".png").exists()) {
+ ret = new javax.swing.ImageIcon(dir + name + ".png");
+ } else {
+ ret = new ImageIcon(ch.psi.pshell.ui.App.class.getResource("/ch/psi/pshell/ui/" + name + ".png"));
+ if (MainFrame.isDark()) {
+ try {
+ ret = new ImageIcon(ch.psi.pshell.ui.App.class.getResource("/ch/psi/pshell/ui/dark/" + name + ".png"));
+ } catch (Exception e) {
+ }
+ }
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ return ret;
+ }
+
+ String getIconName(JButton button) {
+ String ret = button.getIcon().toString();
+ if (ret.indexOf(".") > 0) {
+ ret = ret.substring(0, ret.indexOf("."));
+ }
+ return ret;
+ }
+
+ ////////
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ buttonGroup1 = new javax.swing.ButtonGroup();
+ buttonGroup2 = new javax.swing.ButtonGroup();
+ buttonGroup3 = new javax.swing.ButtonGroup();
+ buttonGroup4 = new javax.swing.ButtonGroup();
+ jProgressBar1 = new javax.swing.JProgressBar();
+ sidePanel = new javax.swing.JPanel();
+ jPanel3 = new javax.swing.JPanel();
+ buttonZoomFit = new javax.swing.JRadioButton();
+ buttonZoomStretch = new javax.swing.JRadioButton();
+ buttonZoomNormal = new javax.swing.JRadioButton();
+ buttonZoom025 = new javax.swing.JRadioButton();
+ buttonZoom05 = new javax.swing.JRadioButton();
+ buttonZoom2 = new javax.swing.JRadioButton();
+ jPanel2 = new javax.swing.JPanel();
+ checkHistogram = new javax.swing.JCheckBox();
+ comboColormap = new javax.swing.JComboBox();
+ jLabel3 = new javax.swing.JLabel();
+ jLabel4 = new javax.swing.JLabel();
+ buttonFullRange = new javax.swing.JRadioButton();
+ buttonManual = new javax.swing.JRadioButton();
+ buttonAutomatic = new javax.swing.JRadioButton();
+ labelMin = new javax.swing.JLabel();
+ spinnerMin = new javax.swing.JSpinner();
+ spinnerMax = new javax.swing.JSpinner();
+ labelMax = new javax.swing.JLabel();
+ btFixColormapRange = new javax.swing.JButton();
+ jPanel5 = new javax.swing.JPanel();
+ buttonServer = new javax.swing.JRadioButton();
+ buttonDirect = new javax.swing.JRadioButton();
+ textState = new javax.swing.JTextField();
+ filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
+ panelScreen = new javax.swing.JPanel();
+ valueScreen = new ch.psi.pshell.swing.DeviceValuePanel();
+ comboScreen = new javax.swing.JComboBox();
+ panelFilter = new javax.swing.JPanel();
+ valueFilter = new ch.psi.pshell.swing.DeviceValuePanel();
+ comboFilter = new javax.swing.JComboBox();
+ panelScreen2 = new javax.swing.JPanel();
+ checkThreshold = new javax.swing.JCheckBox();
+ spinnerThreshold = new javax.swing.JSpinner();
+ checkBackground = new javax.swing.JCheckBox();
+ checkGoodRegion = new javax.swing.JCheckBox();
+ spinnerGrScale = new javax.swing.JSpinner();
+ spinnerGrThreshold = new javax.swing.JSpinner();
+ labelGrThreshold = new javax.swing.JLabel();
+ labelGrScale = new javax.swing.JLabel();
+ panelSlicing = new javax.swing.JPanel();
+ checkSlicing = new javax.swing.JCheckBox();
+ labelSlScale = new javax.swing.JLabel();
+ spinnerSlScale = new javax.swing.JSpinner();
+ labelSlNumber = new javax.swing.JLabel();
+ spinnerSlNumber = new javax.swing.JSpinner();
+ labelSlOrientation = new javax.swing.JLabel();
+ spinnerSlOrientation = new javax.swing.JSpinner();
+ topPanel = new javax.swing.JPanel();
+ toolBar = new javax.swing.JToolBar();
+ buttonSidePanel = new javax.swing.JToggleButton();
+ buttonStreamData = new javax.swing.JButton();
+ buttonSave = new javax.swing.JToggleButton();
+ buttonGrabBackground = new javax.swing.JButton();
+ buttonPause = new javax.swing.JToggleButton();
+ jSeparator6 = new javax.swing.JToolBar.Separator();
+ buttonMarker = new javax.swing.JToggleButton();
+ buttonProfile = new javax.swing.JToggleButton();
+ buttonFit = new javax.swing.JToggleButton();
+ buttonReticle = new javax.swing.JToggleButton();
+ buttonTitle = new javax.swing.JToggleButton();
+ pauseSelection = new ch.psi.pshell.swing.ValueSelection();
+ panelCameraSelection = new javax.swing.JPanel();
+ jLabel1 = new javax.swing.JLabel();
+ comboCameras = new javax.swing.JComboBox();
+ jLabel5 = new javax.swing.JLabel();
+ comboType = new javax.swing.JComboBox();
+ renderer = new ch.psi.pshell.imaging.Renderer();
+
+ setPreferredSize(new java.awt.Dimension(873, 600));
+
+ jPanel3.setBorder(javax.swing.BorderFactory.createTitledBorder("Zoom"));
+
+ buttonGroup1.add(buttonZoomFit);
+ buttonZoomFit.setText("Fit");
+ buttonZoomFit.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonZoomFitActionPerformed(evt);
+ }
+ });
+
+ buttonGroup1.add(buttonZoomStretch);
+ buttonZoomStretch.setText("Stretch");
+ buttonZoomStretch.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonZoomStretchActionPerformed(evt);
+ }
+ });
+
+ buttonGroup1.add(buttonZoomNormal);
+ buttonZoomNormal.setText("Normal");
+ buttonZoomNormal.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonZoomNormalActionPerformed(evt);
+ }
+ });
+
+ buttonGroup1.add(buttonZoom025);
+ buttonZoom025.setText("1/4");
+ buttonZoom025.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonZoom025ActionPerformed(evt);
+ }
+ });
+
+ buttonGroup1.add(buttonZoom05);
+ buttonZoom05.setText("1/2");
+ buttonZoom05.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonZoom05ActionPerformed(evt);
+ }
+ });
+
+ buttonGroup1.add(buttonZoom2);
+ buttonZoom2.setText("2");
+ buttonZoom2.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonZoom2ActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
+ jPanel3.setLayout(jPanel3Layout);
+ jPanel3Layout.setHorizontalGroup(
+ jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel3Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(buttonZoomFit)
+ .addComponent(buttonZoomNormal)
+ .addComponent(buttonZoomStretch))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(buttonZoom025)
+ .addComponent(buttonZoom05)
+ .addComponent(buttonZoom2))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+ jPanel3Layout.setVerticalGroup(
+ jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel3Layout.createSequentialGroup()
+ .addGap(4, 4, 4)
+ .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(buttonZoomNormal)
+ .addComponent(buttonZoom025))
+ .addGap(0, 0, 0)
+ .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(buttonZoomFit)
+ .addComponent(buttonZoom05))
+ .addGap(0, 0, 0)
+ .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(buttonZoomStretch)
+ .addComponent(buttonZoom2))
+ .addContainerGap())
+ );
+
+ jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder("Colormap"));
+
+ checkHistogram.setText("Histogram");
+ checkHistogram.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ checkHistogramActionPerformed(evt);
+ }
+ });
+
+ comboColormap.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ onChangeColormap(evt);
+ }
+ });
+
+ jLabel3.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
+ jLabel3.setText("Type:");
+
+ jLabel4.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
+ jLabel4.setText("Range:");
+
+ buttonGroup3.add(buttonFullRange);
+ buttonFullRange.setText("Full");
+ buttonFullRange.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ onChangeColormap(evt);
+ }
+ });
+
+ buttonGroup3.add(buttonManual);
+ buttonManual.setText("Manual");
+ buttonManual.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ onChangeColormap(evt);
+ }
+ });
+
+ buttonGroup3.add(buttonAutomatic);
+ buttonAutomatic.setText("Automatic");
+ buttonAutomatic.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ onChangeColormap(evt);
+ }
+ });
+
+ labelMin.setText("Min:");
+
+ spinnerMin.setModel(new javax.swing.SpinnerNumberModel(0, 0, 65535, 1));
+ spinnerMin.setEnabled(false);
+ spinnerMin.setPreferredSize(new java.awt.Dimension(77, 20));
+ spinnerMin.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ onChangeColormapRange(evt);
+ }
+ });
+
+ spinnerMax.setModel(new javax.swing.SpinnerNumberModel(255, 0, 65535, 1));
+ spinnerMax.setEnabled(false);
+ spinnerMax.setPreferredSize(new java.awt.Dimension(77, 20));
+ spinnerMax.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ onChangeColormapRange(evt);
+ }
+ });
+
+ labelMax.setText("Max:");
+
+ btFixColormapRange.setText("Fix");
+ btFixColormapRange.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btFixColormapRangeActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
+ jPanel2.setLayout(jPanel2Layout);
+ jPanel2Layout.setHorizontalGroup(
+ jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel2Layout.createSequentialGroup()
+ .addGap(4, 4, 4)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel3)
+ .addComponent(jLabel4))
+ .addGap(4, 4, 4)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(buttonAutomatic)
+ .addComponent(buttonFullRange)
+ .addComponent(buttonManual)
+ .addComponent(comboColormap, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
+ .addComponent(labelMax)
+ .addGap(2, 2, 2)
+ .addComponent(spinnerMax, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addComponent(checkHistogram, javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
+ .addComponent(labelMin)
+ .addGap(2, 2, 2)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(btFixColormapRange, javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(spinnerMin, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
+ .addContainerGap())
+ );
+
+ jPanel2Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {btFixColormapRange, spinnerMax, spinnerMin});
+
+ jPanel2Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {jLabel3, jLabel4});
+
+ jPanel2Layout.setVerticalGroup(
+ jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
+ .addGap(4, 4, 4)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(comboColormap, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel3)
+ .addComponent(checkHistogram))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(buttonAutomatic)
+ .addComponent(jLabel4)
+ .addComponent(btFixColormapRange, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGap(0, 0, 0)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(labelMin)
+ .addComponent(spinnerMin, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(buttonFullRange))
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(buttonManual)
+ .addComponent(labelMax)
+ .addComponent(spinnerMax, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addContainerGap())
+ );
+
+ jPanel5.setBorder(javax.swing.BorderFactory.createTitledBorder("Source"));
+
+ buttonGroup4.add(buttonServer);
+ buttonServer.setSelected(true);
+ buttonServer.setText("Server");
+ buttonServer.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonServerActionPerformed(evt);
+ }
+ });
+
+ buttonGroup4.add(buttonDirect);
+ buttonDirect.setText("Direct");
+ buttonDirect.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonDirectActionPerformed(evt);
+ }
+ });
+
+ textState.setEditable(false);
+ textState.setHorizontalAlignment(javax.swing.JTextField.CENTER);
+ textState.setDisabledTextColor(new java.awt.Color(0, 0, 0));
+ textState.setEnabled(false);
+
+ javax.swing.GroupLayout jPanel5Layout = new javax.swing.GroupLayout(jPanel5);
+ jPanel5.setLayout(jPanel5Layout);
+ jPanel5Layout.setHorizontalGroup(
+ jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel5Layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(buttonServer)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(buttonDirect)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(textState, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap())
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel5Layout.createSequentialGroup()
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(56, 56, 56))
+ );
+ jPanel5Layout.setVerticalGroup(
+ jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel5Layout.createSequentialGroup()
+ .addGap(4, 4, 4)
+ .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel5Layout.createSequentialGroup()
+ .addComponent(textState, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(buttonServer)
+ .addComponent(buttonDirect)))
+ .addContainerGap())
+ );
+
+ panelScreen.setBorder(javax.swing.BorderFactory.createTitledBorder("Screen"));
+
+ comboScreen.setEnabled(false);
+ comboScreen.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ comboScreenActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout panelScreenLayout = new javax.swing.GroupLayout(panelScreen);
+ panelScreen.setLayout(panelScreenLayout);
+ panelScreenLayout.setHorizontalGroup(
+ panelScreenLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelScreenLayout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(panelScreenLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(valueScreen, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(comboScreen, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addContainerGap())
+ );
+ panelScreenLayout.setVerticalGroup(
+ panelScreenLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelScreenLayout.createSequentialGroup()
+ .addGap(4, 4, 4)
+ .addComponent(comboScreen, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(valueScreen, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap())
+ );
+
+ panelFilter.setBorder(javax.swing.BorderFactory.createTitledBorder("Filter"));
+
+ comboFilter.setEnabled(false);
+ comboFilter.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ comboFilterActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout panelFilterLayout = new javax.swing.GroupLayout(panelFilter);
+ panelFilter.setLayout(panelFilterLayout);
+ panelFilterLayout.setHorizontalGroup(
+ panelFilterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelFilterLayout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(panelFilterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(valueFilter, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(comboFilter, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addContainerGap())
+ );
+ panelFilterLayout.setVerticalGroup(
+ panelFilterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelFilterLayout.createSequentialGroup()
+ .addGap(4, 4, 4)
+ .addComponent(comboFilter, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(valueFilter, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+
+ panelScreen2.setBorder(javax.swing.BorderFactory.createTitledBorder("Pipeline"));
+
+ checkThreshold.setText("Threshold");
+ checkThreshold.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ checkThresholdActionPerformed(evt);
+ }
+ });
+
+ spinnerThreshold.setModel(new javax.swing.SpinnerNumberModel(0.0d, 0.0d, 99999.0d, 1.0d));
+ spinnerThreshold.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ spinnerThresholdonChange(evt);
+ }
+ });
+
+ checkBackground.setText("Subtract Background");
+ checkBackground.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ checkBackgroundActionPerformed(evt);
+ }
+ });
+
+ checkGoodRegion.setText("Good Region");
+ checkGoodRegion.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ checkGoodRegionActionPerformed(evt);
+ }
+ });
+
+ spinnerGrScale.setModel(new javax.swing.SpinnerNumberModel(3.0d, 0.01d, 99999.0d, 1.0d));
+ spinnerGrScale.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ spinnerGrThresholdonChange(evt);
+ }
+ });
+
+ spinnerGrThreshold.setModel(new javax.swing.SpinnerNumberModel(0.5d, 0.04d, 1.0d, 0.1d));
+ spinnerGrThreshold.setPreferredSize(new java.awt.Dimension(92, 20));
+ spinnerGrThreshold.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ spinnerGrThresholdonChange(evt);
+ }
+ });
+
+ labelGrThreshold.setText("Threshold:");
+
+ labelGrScale.setText("Scale:");
+
+ checkSlicing.setText("Slicing");
+ checkSlicing.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ checkSlicingActionPerformed(evt);
+ }
+ });
+
+ labelSlScale.setText("Scale:");
+
+ spinnerSlScale.setModel(new javax.swing.SpinnerNumberModel(3.0d, 0.01d, 99999.0d, 1.0d));
+ spinnerSlScale.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ spinnerSlicingChange(evt);
+ }
+ });
+
+ labelSlNumber.setText("Slices:");
+
+ spinnerSlNumber.setModel(new javax.swing.SpinnerNumberModel(2, 0, 1000, 1));
+ spinnerSlNumber.setPreferredSize(new java.awt.Dimension(92, 20));
+ spinnerSlNumber.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ spinnerSlicingChange(evt);
+ }
+ });
+
+ labelSlOrientation.setText("Orientation:");
+
+ spinnerSlOrientation.setModel(new javax.swing.SpinnerListModel(new String[] {"vertical", "horizontal"}));
+ spinnerSlOrientation.setPreferredSize(new java.awt.Dimension(92, 20));
+ spinnerSlOrientation.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ spinnerSlicingChange(evt);
+ }
+ });
+
+ javax.swing.GroupLayout panelSlicingLayout = new javax.swing.GroupLayout(panelSlicing);
+ panelSlicing.setLayout(panelSlicingLayout);
+ panelSlicingLayout.setHorizontalGroup(
+ panelSlicingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelSlicingLayout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(panelSlicingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelSlicingLayout.createSequentialGroup()
+ .addComponent(checkSlicing)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(panelSlicingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelSlicingLayout.createSequentialGroup()
+ .addComponent(labelSlNumber)
+ .addGap(2, 2, 2)
+ .addComponent(spinnerSlNumber, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelSlicingLayout.createSequentialGroup()
+ .addComponent(labelSlScale)
+ .addGap(2, 2, 2)
+ .addComponent(spinnerSlScale, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelSlicingLayout.createSequentialGroup()
+ .addGap(0, 0, Short.MAX_VALUE)
+ .addComponent(labelSlOrientation)
+ .addGap(2, 2, 2)
+ .addComponent(spinnerSlOrientation, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ .addContainerGap())
+ );
+
+ panelSlicingLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {spinnerSlNumber, spinnerSlOrientation, spinnerSlScale});
+
+ panelSlicingLayout.setVerticalGroup(
+ panelSlicingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelSlicingLayout.createSequentialGroup()
+ .addGroup(panelSlicingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(checkSlicing)
+ .addGroup(panelSlicingLayout.createSequentialGroup()
+ .addGroup(panelSlicingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(spinnerSlNumber, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(labelSlNumber))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(panelSlicingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(spinnerSlScale, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(labelSlScale))))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(panelSlicingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(spinnerSlOrientation, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(labelSlOrientation)))
+ );
+
+ javax.swing.GroupLayout panelScreen2Layout = new javax.swing.GroupLayout(panelScreen2);
+ panelScreen2.setLayout(panelScreen2Layout);
+ panelScreen2Layout.setHorizontalGroup(
+ panelScreen2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelScreen2Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(panelScreen2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelScreen2Layout.createSequentialGroup()
+ .addComponent(checkThreshold)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelScreen2Layout.createSequentialGroup()
+ .addGroup(panelScreen2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelScreen2Layout.createSequentialGroup()
+ .addComponent(checkGoodRegion)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(labelGrScale))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelScreen2Layout.createSequentialGroup()
+ .addGap(0, 0, Short.MAX_VALUE)
+ .addComponent(labelGrThreshold)))
+ .addGap(2, 2, 2)))
+ .addGroup(panelScreen2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(spinnerGrThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(spinnerGrScale, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(spinnerThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addContainerGap())
+ .addComponent(panelSlicing, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(panelScreen2Layout.createSequentialGroup()
+ .addGap(6, 6, 6)
+ .addComponent(checkBackground)
+ .addGap(106, 106, 106))
+ );
+
+ panelScreen2Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {spinnerGrScale, spinnerGrThreshold, spinnerThreshold});
+
+ panelScreen2Layout.setVerticalGroup(
+ panelScreen2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelScreen2Layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(checkBackground)
+ .addGap(2, 2, 2)
+ .addGroup(panelScreen2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(checkThreshold)
+ .addComponent(spinnerThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGap(2, 2, 2)
+ .addGroup(panelScreen2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(checkGoodRegion)
+ .addComponent(spinnerGrScale, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(labelGrScale))
+ .addGap(2, 2, 2)
+ .addGroup(panelScreen2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(spinnerGrThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(labelGrThreshold))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(panelSlicing, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap())
+ );
+
+ javax.swing.GroupLayout sidePanelLayout = new javax.swing.GroupLayout(sidePanel);
+ sidePanel.setLayout(sidePanelLayout);
+ sidePanelLayout.setHorizontalGroup(
+ sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(sidePanelLayout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
+ .addComponent(jPanel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jPanel3, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jPanel2, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(panelScreen2, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(panelScreen, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(panelFilter, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+ sidePanelLayout.setVerticalGroup(
+ sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(sidePanelLayout.createSequentialGroup()
+ .addGap(0, 0, 0)
+ .addComponent(jPanel5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(panelScreen2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(panelScreen, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(panelFilter, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+
+ toolBar.setFloatable(false);
+ toolBar.setRollover(true);
+
+ buttonSidePanel.setIcon(getIcon("List"));
+ buttonSidePanel.setSelected(true);
+ buttonSidePanel.setText(" ");
+ buttonSidePanel.setToolTipText("Show Side Panel");
+ buttonSidePanel.setFocusable(false);
+ buttonSidePanel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonSidePanel.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonSidePanelActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonSidePanel);
+
+ buttonStreamData.setIcon(getIcon("Details"));
+ buttonStreamData.setText(" ");
+ buttonStreamData.setToolTipText("Show Data Window");
+ buttonStreamData.setFocusable(false);
+ buttonStreamData.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonStreamData.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonStreamDataActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonStreamData);
+
+ buttonSave.setIcon(getIcon("Save"));
+ buttonSave.setText(" ");
+ buttonSave.setToolTipText("Save Snapshot");
+ buttonSave.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonSave.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonSaveActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonSave);
+
+ buttonGrabBackground.setIcon(getIcon("Background"));
+ buttonGrabBackground.setText(" ");
+ buttonGrabBackground.setToolTipText("Grab Background");
+ buttonGrabBackground.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonGrabBackground.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonGrabBackgroundActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonGrabBackground);
+
+ buttonPause.setIcon(getIcon("Pause"));
+ buttonPause.setText(" ");
+ buttonPause.setToolTipText("Pause");
+ buttonPause.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonPause.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonPauseActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonPause);
+
+ jSeparator6.setMaximumSize(new java.awt.Dimension(20, 32767));
+ jSeparator6.setPreferredSize(new java.awt.Dimension(20, 0));
+ jSeparator6.setRequestFocusEnabled(false);
+ toolBar.add(jSeparator6);
+
+ buttonMarker.setIcon(getIcon("Marker"));
+ buttonMarker.setText(" ");
+ buttonMarker.setToolTipText("Show Marker");
+ buttonMarker.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonMarker.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonMarkerActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonMarker);
+
+ buttonProfile.setIcon(getIcon("Profile"
+ + ""));
+ buttonProfile.setSelected(true);
+ buttonProfile.setText(" ");
+ buttonProfile.setToolTipText("Show Image Profile");
+ buttonProfile.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonProfile.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonProfileActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonProfile);
+
+ buttonFit.setIcon(getIcon("Fit"));
+ buttonFit.setSelected(true);
+ buttonFit.setText(" ");
+ buttonFit.setToolTipText("Show Fit");
+ buttonFit.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonFit.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonFitActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonFit);
+
+ buttonReticle.setIcon(getIcon("Reticule"));
+ buttonReticle.setSelected(true);
+ buttonReticle.setText(" ");
+ buttonReticle.setToolTipText("Show Reticle");
+ buttonReticle.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonReticle.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonReticleActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonReticle);
+
+ buttonTitle.setIcon(getIcon("Title"));
+ buttonTitle.setText(" ");
+ buttonTitle.setToolTipText("Show Camera Name");
+ buttonTitle.setFocusable(false);
+ buttonTitle.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+ buttonTitle.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ buttonTitleActionPerformed(evt);
+ }
+ });
+ toolBar.add(buttonTitle);
+
+ pauseSelection.setDecimals(0);
+
+ jLabel1.setText("Camera:");
+
+ comboCameras.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N
+ comboCameras.setMaximumRowCount(30);
+ comboCameras.setMinimumSize(new java.awt.Dimension(127, 27));
+ comboCameras.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ comboCamerasActionPerformed(evt);
+ }
+ });
+
+ jLabel5.setText("Type:");
+
+ comboType.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N
+ comboType.setMaximumRowCount(30);
+ comboType.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "All", "Laser", "Electrons", "Photonics" }));
+ comboType.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ comboTypeActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout panelCameraSelectionLayout = new javax.swing.GroupLayout(panelCameraSelection);
+ panelCameraSelection.setLayout(panelCameraSelectionLayout);
+ panelCameraSelectionLayout.setHorizontalGroup(
+ panelCameraSelectionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelCameraSelectionLayout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(jLabel1)
+ .addGap(0, 0, 0)
+ .addComponent(comboCameras, javax.swing.GroupLayout.PREFERRED_SIZE, 222, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jLabel5)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(comboType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(0, 0, 0))
+ );
+ panelCameraSelectionLayout.setVerticalGroup(
+ panelCameraSelectionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(panelCameraSelectionLayout.createSequentialGroup()
+ .addGap(0, 0, 0)
+ .addGroup(panelCameraSelectionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(comboType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel5)
+ .addComponent(jLabel1)
+ .addComponent(comboCameras, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGap(0, 0, 0))
+ );
+
+ javax.swing.GroupLayout topPanelLayout = new javax.swing.GroupLayout(topPanel);
+ topPanel.setLayout(topPanelLayout);
+ topPanelLayout.setHorizontalGroup(
+ topPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, topPanelLayout.createSequentialGroup()
+ .addComponent(toolBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(18, 18, 18)
+ .addComponent(panelCameraSelection, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGap(18, 18, 18)
+ .addComponent(pauseSelection, javax.swing.GroupLayout.PREFERRED_SIZE, 334, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap())
+ );
+ topPanelLayout.setVerticalGroup(
+ topPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(topPanelLayout.createSequentialGroup()
+ .addGap(1, 1, 1)
+ .addGroup(topPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(pauseSelection, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(toolBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(panelCameraSelection, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ );
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
+ .addComponent(sidePanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(renderer, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(topPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(topPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(sidePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(renderer, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
+ );
+ }// //GEN-END:initComponents
+
+ private void buttonZoomFitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonZoomFitActionPerformed
+ try {
+ renderer.setMode(RendererMode.Fit);
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonZoomFitActionPerformed
+
+ private void buttonZoomStretchActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonZoomStretchActionPerformed
+ try {
+ renderer.setMode(RendererMode.Stretch);
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonZoomStretchActionPerformed
+
+ private void buttonZoomNormalActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonZoomNormalActionPerformed
+ try {
+ renderer.setMode(RendererMode.Fixed);
+ centralizeRenderer();
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonZoomNormalActionPerformed
+
+ private void onChangeColormap(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onChangeColormap
+ try {
+ if ((camera != null) && (camera instanceof ColormapSource) && !updatingColormap) {
+ ColormapSource source = (ColormapSource) camera;
+ Color colorReticule = new Color(16, 16, 16);
+ Color colorMarker = new Color(128, 128, 128);
+ Colormap colormap = (Colormap) comboColormap.getSelectedItem();
+ source.getConfig().colormap = (colormap == null) ? Colormap.Flame : colormap;
+ switch (source.getConfig().colormap) {
+ case Grayscale:
+ case Inverted:
+ case Red:
+ case Green:
+ case Blue:
+ colorReticule = new Color(0, 192, 0);
+ colorMarker = new Color(64, 255, 64);
+ break;
+ case Flame:
+ colorReticule = new Color(0, 192, 0);
+ colorMarker = new Color(64, 255, 64);
+ break;
+ }
+
+ renderer.setPenReticle(new Pen(colorReticule));
+ renderer.setPenProfile(new Pen(colorReticule, 0));
+ renderer.setPenMarker(new Pen(colorMarker, 2));
+ renderer.setShowReticle(false);
+ checkReticle();
+ source.getConfig().colormapAutomatic = buttonAutomatic.isSelected();
+ source.getConfig().colormapMin = buttonFullRange.isSelected() ? Double.NaN : (Integer) spinnerMin.getValue();
+ source.getConfig().colormapMax = buttonFullRange.isSelected() ? Double.NaN : (Integer) spinnerMax.getValue();
+ try {
+ source.getConfig().save();
+ } catch (Exception ex) {
+ Logger.getLogger(getClass().getName()).log(Level.WARNING, null, ex);
+ }
+ source.refresh();
+ if (buttonPause.isSelected()) {
+ updatePause();
+ }
+ updateColormap();
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_onChangeColormap
+
+ private void onChangeColormapRange(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_onChangeColormapRange
+ onChangeColormap(null);
+ }//GEN-LAST:event_onChangeColormapRange
+
+ private void buttonZoom025ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonZoom025ActionPerformed
+ renderer.setZoom(0.25);
+ renderer.setMode(RendererMode.Zoom);
+ centralizeRenderer();
+ }//GEN-LAST:event_buttonZoom025ActionPerformed
+
+ private void buttonZoom05ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonZoom05ActionPerformed
+ renderer.setZoom(0.5);
+ renderer.setMode(RendererMode.Zoom);
+ centralizeRenderer();
+ }//GEN-LAST:event_buttonZoom05ActionPerformed
+
+ private void buttonServerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonServerActionPerformed
+ if (!usingServer) {
+ usingServer = true;
+ requestCameraListUpdate = true;
+ }
+ comboCamerasActionPerformed(null);
+ }//GEN-LAST:event_buttonServerActionPerformed
+
+ private void buttonDirectActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonDirectActionPerformed
+ if (usingServer) {
+ usingServer = false;
+ requestCameraListUpdate = true;
+ }
+ comboCamerasActionPerformed(null);
+ }//GEN-LAST:event_buttonDirectActionPerformed
+
+ private void comboScreenActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboScreenActionPerformed
+
+ comboScreen.setEnabled(false);
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ ChannelInteger setpoint = null;
+ try {
+ int index = comboScreen.getSelectedIndex();
+ if (index>=0){
+ if (cameraName.contains("DSRM")) {
+ setpoint = new ChannelInteger(null, cameraName + ":POSITION_SP");
+ } else {
+ setpoint = new ChannelInteger(null, cameraName + ":SET_SCREEN1_POS");
+ }
+ setpoint.initialize();
+ Integer readback = setpoint.read();
+ if ((readback==null) || (setpoint.read() != index)) {
+ setpoint.write(index);
+ //Must be threaded to control the laser because of sleep in setLaserState
+ /*
+ boolean laserOn = getLaserState();
+ if (laserOn) {
+ setLaserState(false);
+ }
+ try {
+ setpoint.write(index);
+ } finally {
+ if (laserOn) {
+ setLaserState(true);
+ }
+ }
+ */
+ }
+ screen.read();
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ } finally {
+ comboScreen.setEnabled(true);
+ if (setpoint != null) {
+ setpoint.close();
+ }
+ }
+ }
+ }).start();
+ }//GEN-LAST:event_comboScreenActionPerformed
+
+ private void comboFilterActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboFilterActionPerformed
+ try {
+ String setpoint = (String) comboFilter.getSelectedItem();
+ if (setpoint!=null){
+ if (!setpoint.equals(filter.read())) {
+ filter.write(setpoint);
+ }
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_comboFilterActionPerformed
+
+ private void checkHistogramActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkHistogramActionPerformed
+ try {
+ setHistogramVisible(checkHistogram.isSelected());
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_checkHistogramActionPerformed
+
+ private void buttonZoom2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonZoom2ActionPerformed
+ renderer.setZoom(2.0);
+ renderer.setMode(RendererMode.Zoom);
+ centralizeRenderer();
+ }//GEN-LAST:event_buttonZoom2ActionPerformed
+
+ private void spinnerThresholdonChange(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spinnerThresholdonChange
+ if (!updatingServerControls) {
+ try {
+ if ((server != null) && (server.isStarted())) {
+ server.setThreshold((Double) spinnerThreshold.getValue());
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ updateServerControls();
+ }
+ }
+ }//GEN-LAST:event_spinnerThresholdonChange
+
+ private void checkBackgroundActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkBackgroundActionPerformed
+ if (server != null) {
+ if (!updatingServerControls) {
+ try {
+ if (server.isStarted()) {
+ server.setBackgroundSubtraction(checkBackground.isSelected());
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ updateServerControls();
+ updatingServerControls = true;
+ checkBackground.setSelected(false);
+ updatingServerControls = false;
+
+ }
+ }
+ } else {
+ camera.setBackgroundEnabled(checkBackground.isSelected());
+ }
+ }//GEN-LAST:event_checkBackgroundActionPerformed
+
+ private void checkThresholdActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkThresholdActionPerformed
+ if (!updatingServerControls) {
+ try {
+ if ((server != null) && (server.isStarted())) {
+ spinnerThreshold.setVisible(checkThreshold.isSelected());
+ server.setThreshold(checkThreshold.isSelected() ? (Double) spinnerThreshold.getValue() : null);
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ updateServerControls();
+ }
+ }
+ }//GEN-LAST:event_checkThresholdActionPerformed
+
+ private void checkGoodRegionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkGoodRegionActionPerformed
+ if (!updatingServerControls) {
+ try {
+ if ((server != null) && (server.isStarted())) {
+ goodRegion = checkGoodRegion.isSelected();
+ setGoodRegionOptionsVisible(goodRegion);
+ if (goodRegion) {
+ server.setGoodRegion(((Number) spinnerGrThreshold.getValue()).doubleValue(), ((Number) spinnerGrScale.getValue()).doubleValue());
+ } else {
+ server.setGoodRegion(null);
+ }
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ updateServerControls();
+ }
+ }
+ }//GEN-LAST:event_checkGoodRegionActionPerformed
+
+ private void spinnerGrThresholdonChange(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spinnerGrThresholdonChange
+ if (!updatingServerControls) {
+ try {
+ if ((server != null) && (server.isStarted())) {
+ server.setGoodRegion((Double) spinnerGrThreshold.getValue(), (Double) spinnerGrScale.getValue());
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ updateServerControls();
+ }
+ }
+ }//GEN-LAST:event_spinnerGrThresholdonChange
+
+ private void btFixColormapRangeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btFixColormapRangeActionPerformed
+ try {
+ updatingColormap = true;
+ ArrayProperties properties = currentFrame.data.getProperties();
+ spinnerMax.setValue(properties.max.intValue());
+ spinnerMin.setValue(properties.min.intValue());
+ buttonManual.setSelected(true);
+ } catch (Exception ex) {
+ showException(ex);
+ } finally {
+ updatingColormap = false;
+ onChangeColormap(null);
+ }
+ }//GEN-LAST:event_btFixColormapRangeActionPerformed
+
+ private void checkSlicingActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkSlicingActionPerformed
+ if (!updatingServerControls) {
+ try {
+ if ((server != null) && (server.isStarted())) {
+ slicing = checkSlicing.isSelected();
+ setSlicingOptionsVisible(slicing);
+ if (slicing) {
+ server.setSlicing((Integer) spinnerSlNumber.getValue(), (Double) spinnerSlScale.getValue(), spinnerSlOrientation.getValue().toString());
+ } else {
+ server.setSlicing(null);
+ }
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ updateServerControls();
+ }
+ }
+ }//GEN-LAST:event_checkSlicingActionPerformed
+
+ private void spinnerSlicingChange(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spinnerSlicingChange
+ if (!updatingServerControls) {
+ try {
+ if ((server != null) && (server.isStarted())) {
+ server.setSlicing((Integer) spinnerSlNumber.getValue(), (Double) spinnerSlScale.getValue(), spinnerSlOrientation.getValue().toString());
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ updateServerControls();
+ }
+ }
+ }//GEN-LAST:event_spinnerSlicingChange
+
+ private void buttonReticleActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonReticleActionPerformed
+ try {
+ checkReticle();
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonReticleActionPerformed
+
+ private void buttonFitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonFitActionPerformed
+ try {
+ showFit = buttonFit.isSelected();
+ if (showFit) {
+ renderer.setProfile(Renderer.Profile.None);
+ } else {
+ renderer.removeOverlays(fitOv);
+ fitOv = null;
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonFitActionPerformed
+
+ private void buttonProfileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonProfileActionPerformed
+ try {
+ showProfile = buttonProfile.isSelected();
+ if (showProfile) {
+ renderer.setProfile(Renderer.Profile.None);
+ } else {
+ renderer.removeOverlays(profileOv);
+ profileOv = null;
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonProfileActionPerformed
+
+ private void buttonMarkerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonMarkerActionPerformed
+ try {
+ checkMarker(null);
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonMarkerActionPerformed
+
+ private void buttonPauseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonPauseActionPerformed
+ try {
+ if (!updatingButtons) {
+ removePauseOverlay();
+ if (camera != null) {
+ boolean pause = !renderer.isPaused();
+ synchronized (imageBuffer) {
+ if (pause) {
+ renderer.pause();
+ panelCameraSelection.setVisible(false);
+ pauseSelection.setVisible(true);
+ if (imageBuffer.size() > 0) {
+ pauseSelection.setEnabled(true);
+ pauseSelection.setMaxValue(imageBuffer.size());
+ pauseSelection.setValue(imageBuffer.size());
+ updatePause();
+ } else {
+ pauseSelection.setEnabled(false);
+ pauseSelection.setMaxValue(1);
+ pauseSelection.setValue(1);
+ }
+ } else {
+ imageBuffer.clear();
+ renderer.resume();
+ //renderer.clear();
+ pauseSelection.setVisible(false);
+ panelCameraSelection.setVisible(true);
+ }
+ }
+ updateStreamData();
+ }
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonPauseActionPerformed
+
+ private void buttonGrabBackgroundActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonGrabBackgroundActionPerformed
+ try {
+ if (camera != null) {
+ boolean laserOn = getLaserState();
+ OptionResult ret = null;
+ if (laserOn) {
+ ret = SwingUtils.showOption(getTopLevel(), "Capture Background", "Do you want to put laser on delay for capturing background?", OptionType.YesNoCancel);
+ if (ret == OptionResult.No) {
+ laserOn = false;
+ }
+ } else {
+ ret = SwingUtils.showOption(getTopLevel(), "Capture Background", "Do you want to capture background now?", OptionType.OkCancel);
+ }
+
+ if (ret == OptionResult.Cancel) {
+ return;
+ }
+
+ if (laserOn) {
+ setLaserState(false);
+ }
+ try {
+ System.out.println("Grabbing background for: " + cameraName);
+ if (server != null) {
+ server.captureBackground(5);
+ } else {
+ camera.captureBackground(5, 0);
+ }
+ } finally {
+ if (laserOn) {
+ setLaserState(true);
+ }
+ }
+ SwingUtils.showMessage(getTopLevel(), "Success", "Success capturing background", 5000);
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonGrabBackgroundActionPerformed
+
+ private void buttonSaveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonSaveActionPerformed
+ try {
+ saveSnapshot();
+ } catch (Exception ex) {
+ Logger.getLogger(getClass().getName()).log(Level.WARNING, null, ex);
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonSaveActionPerformed
+
+ private void buttonStreamDataActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonStreamDataActionPerformed
+ try {
+ showStreamData();
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_buttonStreamDataActionPerformed
+
+ private void buttonSidePanelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonSidePanelActionPerformed
+ sidePanel.setVisible(buttonSidePanel.isSelected());
+ }//GEN-LAST:event_buttonSidePanelActionPerformed
+
+ private void comboTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboTypeActionPerformed
+ try {
+ updateCameraList();
+ if ((cameraName != null) && (!cameraName.equals(comboCameras.getSelectedItem()))) {
+ setCamera(null);
+ } else {
+ setCamera(cameraName);
+ }
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ } finally {
+ updateButtons();
+ }
+ }//GEN-LAST:event_comboTypeActionPerformed
+
+ private void comboCamerasActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboCamerasActionPerformed
+ try {
+ if (!updatingCameraSelection) {
+ if (!comboCameras.isEnabled()) {
+ throw new Exception("Invalid state");
+ }
+ comboCameras.setEnabled(false);
+ comboType.setEnabled(false);
+ buttonServer.setEnabled(false);
+ buttonDirect.setEnabled(false);
+ final String cameraName = (String) comboCameras.getSelectedItem();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ if (requestCameraListUpdate) {
+ requestCameraListUpdate = false;
+ try {
+ updateCameraList();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ try {
+ setCamera(cameraName.trim().isEmpty() ? null : cameraName);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ } finally {
+ updateButtons();
+ comboCameras.setEnabled(true);
+ comboType.setEnabled(true);
+ buttonServer.setEnabled(true);
+ buttonDirect.setEnabled(true);
+ }
+ }
+ }).start();
+ }
+ } catch (Exception ex) {
+ showException(ex);
+ }
+ }//GEN-LAST:event_comboCamerasActionPerformed
+
+ private void buttonTitleActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonTitleActionPerformed
+ try {
+ manageTitleOverlay();
+ } catch (Exception ex) {
+ showException(ex);
+ } finally {
+ }
+ }//GEN-LAST:event_buttonTitleActionPerformed
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton btFixColormapRange;
+ private javax.swing.JRadioButton buttonAutomatic;
+ private javax.swing.JRadioButton buttonDirect;
+ private javax.swing.JToggleButton buttonFit;
+ private javax.swing.JRadioButton buttonFullRange;
+ private javax.swing.JButton buttonGrabBackground;
+ private javax.swing.ButtonGroup buttonGroup1;
+ private javax.swing.ButtonGroup buttonGroup2;
+ private javax.swing.ButtonGroup buttonGroup3;
+ private javax.swing.ButtonGroup buttonGroup4;
+ private javax.swing.JRadioButton buttonManual;
+ private javax.swing.JToggleButton buttonMarker;
+ private javax.swing.JToggleButton buttonPause;
+ private javax.swing.JToggleButton buttonProfile;
+ private javax.swing.JToggleButton buttonReticle;
+ private javax.swing.JToggleButton buttonSave;
+ private javax.swing.JRadioButton buttonServer;
+ private javax.swing.JToggleButton buttonSidePanel;
+ private javax.swing.JButton buttonStreamData;
+ private javax.swing.JToggleButton buttonTitle;
+ private javax.swing.JRadioButton buttonZoom025;
+ private javax.swing.JRadioButton buttonZoom05;
+ private javax.swing.JRadioButton buttonZoom2;
+ private javax.swing.JRadioButton buttonZoomFit;
+ private javax.swing.JRadioButton buttonZoomNormal;
+ private javax.swing.JRadioButton buttonZoomStretch;
+ private javax.swing.JCheckBox checkBackground;
+ private javax.swing.JCheckBox checkGoodRegion;
+ private javax.swing.JCheckBox checkHistogram;
+ private javax.swing.JCheckBox checkSlicing;
+ private javax.swing.JCheckBox checkThreshold;
+ private javax.swing.JComboBox comboCameras;
+ private javax.swing.JComboBox comboColormap;
+ private javax.swing.JComboBox comboFilter;
+ private javax.swing.JComboBox comboScreen;
+ private javax.swing.JComboBox comboType;
+ private javax.swing.Box.Filler filler1;
+ private javax.swing.JLabel jLabel1;
+ private javax.swing.JLabel jLabel3;
+ private javax.swing.JLabel jLabel4;
+ private javax.swing.JLabel jLabel5;
+ private javax.swing.JPanel jPanel2;
+ private javax.swing.JPanel jPanel3;
+ private javax.swing.JPanel jPanel5;
+ private javax.swing.JProgressBar jProgressBar1;
+ private javax.swing.JToolBar.Separator jSeparator6;
+ private javax.swing.JLabel labelGrScale;
+ private javax.swing.JLabel labelGrThreshold;
+ private javax.swing.JLabel labelMax;
+ private javax.swing.JLabel labelMin;
+ private javax.swing.JLabel labelSlNumber;
+ private javax.swing.JLabel labelSlOrientation;
+ private javax.swing.JLabel labelSlScale;
+ private javax.swing.JPanel panelCameraSelection;
+ private javax.swing.JPanel panelFilter;
+ private javax.swing.JPanel panelScreen;
+ private javax.swing.JPanel panelScreen2;
+ private javax.swing.JPanel panelSlicing;
+ private ch.psi.pshell.swing.ValueSelection pauseSelection;
+ private ch.psi.pshell.imaging.Renderer renderer;
+ private javax.swing.JPanel sidePanel;
+ private javax.swing.JSpinner spinnerGrScale;
+ private javax.swing.JSpinner spinnerGrThreshold;
+ private javax.swing.JSpinner spinnerMax;
+ private javax.swing.JSpinner spinnerMin;
+ private javax.swing.JSpinner spinnerSlNumber;
+ private javax.swing.JSpinner spinnerSlOrientation;
+ private javax.swing.JSpinner spinnerSlScale;
+ private javax.swing.JSpinner spinnerThreshold;
+ private javax.swing.JTextField textState;
+ private javax.swing.JToolBar toolBar;
+ private javax.swing.JPanel topPanel;
+ private ch.psi.pshell.swing.DeviceValuePanel valueFilter;
+ private ch.psi.pshell.swing.DeviceValuePanel valueScreen;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/plugins/TestScan.form b/plugins/TestScan.form
index 1eca698..d81e623 100755
--- a/plugins/TestScan.form
+++ b/plugins/TestScan.form
@@ -109,7 +109,7 @@
-
+
diff --git a/plugins/TestScan.java b/plugins/TestScan.java
index 70ab360..d120bc0 100755
--- a/plugins/TestScan.java
+++ b/plugins/TestScan.java
@@ -63,7 +63,7 @@ public class TestScan extends Panel {
jLabel3.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
jLabel3.setText("Steps:");
- spinnerSteps.setModel(new javax.swing.SpinnerNumberModel(0, 0, 100, 1));
+ spinnerSteps.setModel(new javax.swing.SpinnerNumberModel(10, 0, 100, 1));
buttonStart.setText("Start");
buttonStart.addActionListener(new java.awt.event.ActionListener() {
@@ -149,9 +149,14 @@ public class TestScan extends Panel {
pars.add(spinnerStart.getValue());
pars.add(spinnerEnd.getValue());
pars.add(spinnerSteps.getValue());
- runAsync("TestScan", pars);
+ runAsync("test/TestScan", pars).handle((ret, ex)->{
+ if (ex != null){
+ showException((Exception)ex);
+ }
+ return ret;
+ });
} catch (Exception ex) {
- Logger.getLogger(TestScan.class.getName()).log(Level.SEVERE, null, ex);
+ showException((Exception)ex);
}
}//GEN-LAST:event_buttonStartActionPerformed
diff --git a/plugins/a.form b/plugins/a.form
new file mode 100644
index 0000000..ff9cfab
--- /dev/null
+++ b/plugins/a.form
@@ -0,0 +1,28 @@
+
+
+
diff --git a/plugins/a.java b/plugins/a.java
new file mode 100644
index 0000000..751a562
--- /dev/null
+++ b/plugins/a.java
@@ -0,0 +1,52 @@
+import ch.psi.pshell.ui.Panel;
+import ch.psi.utils.State;
+
+/**
+ *
+ */
+public class a extends Panel {
+
+ public a() {
+ initComponents();
+ }
+
+ //Overridable callbacks
+ @Override
+ public void onInitialize(int runCount) {
+
+ }
+
+ @Override
+ public void onStateChange(State state, State former) {
+
+ }
+
+ @Override
+ public void onExecutedFile(String fileName, Object result) {
+ }
+
+
+ //Callback to perform update - in event thread
+ @Override
+ protected void doUpdate() {
+ }
+
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGap(0, 449, Short.MAX_VALUE)
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGap(0, 137, Short.MAX_VALUE)
+ );
+ }// //GEN-END:initComponents
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/script/2019_0227_023309_XAS_Fe_b.xml b/script/2019_0227_023309_XAS_Fe_b.xml
new file mode 100644
index 0000000..6a84cde
--- /dev/null
+++ b/script/2019_0227_023309_XAS_Fe_b.xml
@@ -0,0 +1,188 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 7000.0
+ 7001.0
+ 1.0
+
+
+
+ 7003.0
+ 7100.0
+ 3.0
+
+
+
+ 7103.0
+ 7160.0
+ 0.3
+
+
+
+ 7160.5
+ 7200.0
+ 0.5
+
+
+
+ 4.80379024078
+ 10.0
+ 0.05
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -2.25
+
+
+ 1.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/script/2019_0227_023309_XAS_Fe_b_TST.xml b/script/2019_0227_023309_XAS_Fe_b_TST.xml
new file mode 100644
index 0000000..ffee909
--- /dev/null
+++ b/script/2019_0227_023309_XAS_Fe_b_TST.xml
@@ -0,0 +1,188 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 7000.0
+ 7001.0
+ 1.0
+
+
+
+ 7003.0
+ 7100.0
+ 3.0
+
+
+
+ 7103.0
+ 7160.0
+ 0.3
+
+
+
+ 7160.5
+ 7200.0
+ 0.5
+
+
+
+ 4.80379024078
+ 10.0
+ 0.05
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -2.25
+
+
+ 1.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/script/CamtoolAccess.py b/script/CamtoolAccess.py
index a3084b3..cdfee2a 100755
--- a/script/CamtoolAccess.py
+++ b/script/CamtoolAccess.py
@@ -2,8 +2,7 @@ import ch.psi.pshell.bs.Scalar as Scalar
import ch.psi.pshell.bs.Waveform as Waveform
import ch.psi.pshell.bs.Stream as Stream
import ch.psi.pshell.bs.Provider as Provider
-
-
+a
url = camtool.getInstance(camtool.getInstances()[0])["stream"]
p=Provider(None, url)
s1 = Stream("stream1", p)
diff --git a/script/PID.py b/script/PID.py
new file mode 100644
index 0000000..ace8d71
--- /dev/null
+++ b/script/PID.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+#
+# This file is part of IvPID.
+# Copyright (C) 2015 Ivmech Mechatronics Ltd.
+#
+# IvPID 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.
+#
+# IvPID 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 .
+
+# title :PID.py
+# description :python pid controller
+# author :Caner Durmusoglu
+# date :20151218
+# version :0.1
+# notes :
+# python_version :2.7
+# ==============================================================================
+
+"""Ivmech PID Controller is simple implementation of a Proportional-Integral-Derivative (PID) Controller in the Python Programming Language.
+More information about PID Controller: http://en.wikipedia.org/wiki/PID_controller
+"""
+import time
+
+class PID:
+ """PID Controller
+ """
+
+ def __init__(self, P=0.2, I=0.0, D=0.0):
+
+ self.Kp = P
+ self.Ki = I
+ self.Kd = D
+
+ self.sample_time = 0.00
+ self.current_time = time.time()
+ self.last_time = self.current_time
+
+ self.clear()
+
+ def clear(self):
+ """Clears PID computations and coefficients"""
+ self.SetPoint = 0.0
+
+ self.PTerm = 0.0
+ self.ITerm = 0.0
+ self.DTerm = 0.0
+ self.last_error = 0.0
+
+ # Windup Guard
+ self.int_error = 0.0
+ self.windup_guard = 20.0
+
+ self.output = 0.0
+
+ def update(self, feedback_value):
+ """Calculates PID value for given reference feedback
+
+ .. math::
+ u(t) = K_p e(t) + K_i \int_{0}^{t} e(t)dt + K_d {de}/{dt}
+
+ .. figure:: images/pid_1.png
+ :align: center
+
+ Test PID with Kp=1.2, Ki=1, Kd=0.001 (test_pid.py)
+
+ """
+ error = self.SetPoint - feedback_value
+
+ self.current_time = time.time()
+ delta_time = self.current_time - self.last_time
+ delta_error = error - self.last_error
+
+ if (delta_time >= self.sample_time):
+ self.PTerm = self.Kp * error
+ self.ITerm += error * delta_time
+
+ if (self.ITerm < -self.windup_guard):
+ self.ITerm = -self.windup_guard
+ elif (self.ITerm > self.windup_guard):
+ self.ITerm = self.windup_guard
+
+ self.DTerm = 0.0
+ if delta_time > 0:
+ self.DTerm = delta_error / delta_time
+
+ # Remember last time and last error for next calculation
+ self.last_time = self.current_time
+ self.last_error = error
+
+ self.output = self.PTerm + (self.Ki * self.ITerm) + (self.Kd * self.DTerm)
+
+ def setKp(self, proportional_gain):
+ """Determines how aggressively the PID reacts to the current error with setting Proportional Gain"""
+ self.Kp = proportional_gain
+
+ def setKi(self, integral_gain):
+ """Determines how aggressively the PID reacts to the current error with setting Integral Gain"""
+ self.Ki = integral_gain
+
+ def setKd(self, derivative_gain):
+ """Determines how aggressively the PID reacts to the current error with setting Derivative Gain"""
+ self.Kd = derivative_gain
+
+ def setWindup(self, windup):
+ """Integral windup, also known as integrator windup or reset windup,
+ refers to the situation in a PID feedback controller where
+ a large change in setpoint occurs (say a positive change)
+ and the integral terms accumulates a significant error
+ during the rise (windup), thus overshooting and continuing
+ to increase as this accumulated error is unwound
+ (offset by errors in the other direction).
+ The specific problem is the excess overshooting.
+ """
+ self.windup_guard = windup
+
+ def setSampleTime(self, sample_time):
+ """PID that should be updated at a regular interval.
+ Based on a pre-determined sampe time, the PID decides if it should compute or return immediately.
+ """
+ self.sample_time = sample_time
\ No newline at end of file
diff --git a/script/RecordInvalidation.py b/script/RecordInvalidation.py
index 8507ec8..c14191e 100755
--- a/script/RecordInvalidation.py
+++ b/script/RecordInvalidation.py
@@ -1,18 +1,15 @@
###################################################################################################
-#Resampling a scan record if no beam
+#Resampling a scan record if there is no beam
###################################################################################################
-
-
-index=0
-
def before_sampling(rec):
- while beam_ok.read() == "No":
+ while beam_ok.read() == False:
time.sleep(0.1)
def after_sampling(rec):
- if beam_ok.read() == "No":
+ if beam_ok.read() == False:
rec.invalidate()
-
-a= lscan((m1), (ai1,ai2), (0,), (0.4,), 20, 0.2, before_read=before_sampling, after_read=after_sampling)
\ No newline at end of file
+ret = lscan(motor, (out, sin), 0.0, 2.0, 0.1, 0.2,\
+ before_read=before_sampling, \
+ after_read=after_sampling)
diff --git a/script/Test.xml b/script/Test.xml
new file mode 100644
index 0000000..4232761
--- /dev/null
+++ b/script/Test.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+ 0.0
+ 10.0
+ 1.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/script/WireScanner.py b/script/WireScanner.py
new file mode 100644
index 0000000..bb69572
--- /dev/null
+++ b/script/WireScanner.py
@@ -0,0 +1,338 @@
+import ch.psi.pshell.epics.Motor
+
+
+class WireScanInfo(DeviceBase):
+ def __init__(self, name, prefix):
+ DeviceBase.__init__(self, name)
+ self.prefix = prefix
+ self.nb_cycles = Channel(self.prefix + ":NB_CYCL_SP", 'l')
+ self.curr_cycl = Channel(self.prefix + ":CURR_CYCL", 'l', callback = self.on_cycle_change)
+ self.curr_cycl.set_monitored(True)
+ set_device_alias(self.curr_cycl, "current_cycle")
+ self.current_cycle = self.curr_cycl.get()
+ self.status_channels=[]
+ for s in ("SCANNING", "SCAN_DONE", "INITIALIZING", "INIT_DONE", "ABORTED", "ERROR"):
+ c = Channel(self.prefix + ":" + s, 'i', callback = self.on_status_change);
+ c.set_monitored(True)
+ self.status_channels.append(c)
+ self.cycles = self.nb_cycles.get()
+ self.on_status_change(None)
+ self.initialize()
+
+ self.home_offsets = []
+ for s in ("W1X_U0_SP", "W1Y_U0_SP", "W2X_U0_SP", "W2Y_U0_SP", "FOIL_U0_SP"):
+ self.home_offsets.append(caget(self.prefix + ":" +s, 'd'))
+
+ def on_status_change(self, val):
+ try:
+ if self.status_channels[0].get() == 1:
+ self.setCache("Scanning " + str(self.current_cycle) + "/" + str(self.cycles), None)
+ self.setState(State.Busy)
+ elif self.status_channels[1].get() == 1:
+ self.setCache("Scan done", None)
+ self.setState(State.Ready)
+ elif self.status_channels[2].get() == 1:
+ self.setCache("Traveling", None)
+ self.setState(State.Paused)
+ elif self.status_channels[3].get() == 1:
+ self.setCache("At start", None)
+ self.setState(State.Ready)
+ elif self.status_channels[4].get() == 1:
+ self.setCache("Abort", None)
+ self.setState(State.Ready)
+ elif self.status_channels[5].get() == 1:
+ self.setCache("Error", None)
+ self.setState(State.Fault)
+ else:
+ pass #All zero, a transition
+ except:
+ self.setCache("offline", None)
+ self.setState(State.Offline)
+
+ def on_cycle_change(self, val):
+ #print "Wire scan cycle change: ", val
+ self.current_cycle = val
+ self.on_status_change(val)
+
+ def doClose(self):
+ self.nb_cycles.close()
+ self.curr_cycl.close()
+ for c in self.status_channels:
+ c.close()
+
+ def get_wire_pos(self, pos_motor):
+ if (pos_motor is None) or math.isnan(pos_motor):
+ return [pos_motor] * 4
+ w1x = (pos_motor - self.home_offsets[0]) / -(math.sqrt(2))
+ w1y = (pos_motor - self.home_offsets[1]) / (math.sqrt(2))
+ w2x = (pos_motor - self.home_offsets[2]) / -(math.sqrt(2))
+ w2y = (pos_motor - self.home_offsets[3]) / (math.sqrt(2))
+ return [w1x, w1y, w2x, w2y]
+
+ def get_motor_pos(self, pos_wire, wire_type):
+ if (pos_wire is None) or math.isnan(pos_wire):
+ return pos_wire
+ if wire_type == WireScanner.WireX1: return self.home_offsets[0] - pos_wire * math.sqrt(2)
+ if wire_type == WireScanner.WireY1: return self.home_offsets[1] + pos_wire * math.sqrt(2)
+ if wire_type == WireScanner.WireX2: return self.home_offsets[2] - pos_wire * math.sqrt(2)
+ if wire_type == WireScanner.WireY2: return self.home_offsets[3] + pos_wire * math.sqrt(2)
+ return None
+
+ def is_valid(self):
+ return caget(self.prefix + ":VALID", 'i')==1
+
+ def _get_channel(self, bunch, wire, name):
+ return self.prefix + ":B" + str(int(bunch)) + "_" + wire.upper() + "_" + name
+
+ def set_out_com(self, bunch, wire, value):
+ caput(self._get_channel(bunch, wire, "CENTER_OF_MASS"), float(value))
+
+ def set_out_rms(self, bunch, wire, value):
+ caput(self._get_channel(bunch, wire, "RMS"), float(value))
+
+ def set_out_amp(self, bunch, wire, value):
+ caput(self._get_channel(bunch, wire, "FIT_AMPLITUDE"), float(value))
+
+ def set_out_mean(self, bunch, wire, value):
+ caput(self._get_channel(bunch, wire, "FIT_MEAN"), float(value))
+
+ def set_out_off(self, bunch, wire, value):
+ caput(self._get_channel(bunch, wire, "FIT_OFFSET"), float(value))
+
+ def set_out_sigma(self, bunch, wire, value):
+ caput(self._get_channel(bunch, wire, "FIT_STANDARD_DEVIATION"), float(value))
+
+ def set_out_pos(self, bunch, wire, value):
+ caput(self._get_channel(bunch, wire, "POSITION"), to_array(value, 'd'))
+
+ def set_out_samples(self, bunch, wire, value):
+ caput(self._get_channel(bunch, wire, "AMPLITUDE"), to_array(value, 'd'))
+
+ def set_out_gauss(self, bunch, wire, value):
+ caput(self._get_channel(bunch, wire, "FIT_GAUSS_FUNCTION"), to_array(value, 'd'))
+
+
+def new_scan_info_device(name, prefix):
+ return WireScanInfo(name, prefix)
+
+def get_wire_pos(wire_scanner, pos):
+ return wire_scanner.get_wire_pos(pos)
+
+def get_scan_selection(scan_type, index = 0):
+ if scan_type == WireScanner.WireX1: return WireScanner.W1X
+ if scan_type == WireScanner.WireY1: return WireScanner.W1Y
+ if scan_type == WireScanner.WireX2: return WireScanner.W2X
+ if scan_type == WireScanner.WireY2: return WireScanner.W2Y
+ if scan_type == WireScanner.Set1: return WireScanner.W1X if (index==0) else WireScanner.W1Y
+ if scan_type == WireScanner.Set2: return WireScanner.W2X if (index==0) else WireScanner.W2Y
+ return None
+
+class WireScanner(WireScanInfo):
+
+ ScanType = [WireX1, WireY1, WireX2, WireY2, Set1, Set2, BackGarage, BackFoil] = ['X1', 'Y1', 'X2', 'Y2', 'Set1', 'Set2', 'BackgroundGarage', 'BackgroundFoil']
+ Selection = [Garage, W1X, W1Y, W2X, W2Y, Foil] = "GARAGE", "W1X", "W1Y", "W2X", "W2Y", "FOIL"
+
+ def __init__(self, prefix, scan_range, cycles=None, velocity=None, continuous = None):
+ WireScanInfo.__init__(self, "Wire Scan " + prefix, prefix)
+ self.motor = ch.psi.pshell.epics.Motor("WireScanner motor", self.prefix + ":MOTOR_1")
+ self.motor.uploadConfig()
+ self.motor.initialize()
+ #self.motor_bs_readback = Channel(self.prefix + ":ENC_1_BS") #, callback = self.on_readback_change)
+ self.motor_bs_readback = Channel("TESTIOC:TESTCALCOUT:Output")
+ #self.motor_bs_readback.set_monitored(True)
+ self.wire_velocity = Channel(self.prefix + ":SCAN_VELO_SP") #wire coordinates
+ self.motor_velocity = Channel(self.prefix + ":SCAN_M_VELO") #motor coordinates
+ self.travel_velocity = Channel(self.prefix + ":TRAVEL_VELO_SP") #motor coordinates
+ self.wire_sel = Channel(self.prefix + ":WIRE_SP", 'l')
+ self.selection = None
+ self.u0 = None
+ self.offset = None
+ self.range = None
+ self.start = None
+ self.end = None
+ self.scan_range = scan_range
+
+ if velocity is not None:
+ self.set_velocity(velocity)
+ if cycles is not None:
+ self.nb_cycles.write(int(cycles))
+ if continuous is not None:
+ caputq(self.prefix + ":SCAN_MODE_SP", 0 if continuous else 1)
+
+ self.readback = self.motor_bs_readback.get()
+ self.cycles = self.nb_cycles.get()
+ self.velocity = self.wire_velocity.get()
+ self.initialize()
+
+ #def on_readback_change(self, val):
+ # self.readback = val
+
+ def set_velocity(self, velocity):
+ self.wire_velocity.write(float(velocity))
+ self.velocity = self.wire_velocity.get()
+
+ def set_travel_velocity(self):
+ tv = self.travel_velocity.read()
+ self.set_velocity(tv/math.sqrt(2))
+ self.motor.speed=(tv)
+
+
+ def set_selection(self, sel):
+ if not sel in WireScanner.Selection:
+ raise Exception("Invalid Wire Scan selection: " + str(sel))
+ self.selection = sel
+ self.u0 = Channel(self.prefix + ":" + self.selection + "_U0_SP")
+ self.offset = Channel(self.prefix + ":" + self.selection + "_OFF_SP")
+ self.range = Channel(self.prefix + ":" + self.selection + "_RANGE_SP")
+ self.start = Channel(self.prefix + ":" + self.selection + "_START_SP")
+ self.end = Channel(self.prefix + ":" + self.selection + "_END_SP")
+ self.wire_sel.put(WireScanner.Selection.index(sel))
+
+ #Setting parameters
+ if self.scan_range is not None:
+ if sel in ["W1X", "W2X"]: self.start.write(float(self.scan_range[0]))
+ if sel in ["W1Y", "W2Y"]: self.start.write(float(self.scan_range[2]))
+ if sel in ["W1X", "W2X"]: self.end.write(float(self.scan_range[1]))
+ if sel in ["W1Y", "W2Y"]: self.end.write(float(self.scan_range[3]))
+ print "Sel: ", sel
+ print "Range: ", self.scan_range
+ print "Scan start: ", self.start.read()
+ print "End start: ", self.end.read()
+
+
+ def abort(self):
+ caputq(self.prefix + ":ABORT.PROC", 1)
+
+ def init(self, wait=False):
+ #if self.selection is not None:
+ # self.wire_sel.put(WireScanner.Selection.index(self.selection))
+ caputq(self.prefix + ":INIT.PROC", 1)
+ if wait:
+ self.wait_in_selection()
+
+ def park(self, wait=False):
+ caputq(self.prefix + ":GARAGE_SEL.PROC", 1)
+ caputq(self.prefix + ":INIT.PROC", 1)
+ if wait:
+ self.wait_in_selection()
+
+ def wait_in_selection(self):
+ time.sleep(0.5) #Some time for the status change
+ self.waitValue("At start", 60000)
+
+ def scan(self):
+ self.cycles = self.nb_cycles.get()
+ caputq(self.prefix + ":SCAN_WIRE", 1)
+
+ def get_sel_wire_pos(self, pos_motor=None):
+ if pos_motor is None:
+ pos_motor = self.motor_bs_readback.get()
+ wire_pos = self.get_wire_pos(pos_motor)
+ if self.selection == WireScanner.W1X: return wire_pos[0]
+ if self.selection == WireScanner.W1Y: return wire_pos[1]
+ if self.selection == WireScanner.W2X: return wire_pos[2]
+ if self.selection == WireScanner.W2Y: return wire_pos[3]
+ return float('nan')
+
+ def doClose(self):
+ WireScanInfo.doClose(self)
+ self.motor.close()
+ self.motor_bs_readback.close()
+ self.wire_velocity.close()
+ self.motor_velocity.close()
+ self.travel_velocity.close()
+ self.wire_sel.close()
+ if self.u0 is not None: self.u0.close()
+ if self.offset is not None: self.offset.close()
+ if self.range is not None: self.range.close()
+ if self.start is not None: self.start.close()
+ if self.end is not None: self.end.close()
+ """
+ def get_cicle_time(self):
+ range = abs(self.start.get() -self.end.get())
+ speed = self.motor_velocity.get()
+ return (range / speed)
+
+ def get_total_time(self):
+ return self.get_cicle_time() * self.cycles
+ """
+
+
+
+
+prefix="STEST-DWSC"
+scan_range = None
+cycles = 1
+
+if scan_range is None or len(scan_range) !=4:
+ scan_range = [ caget(prefix+":W2X_START_SP", 'd'), \
+ caget(prefix+":W2X_END_SP", 'd'), \
+ caget(prefix+":W2Y_START_SP", 'd'), \
+ caget(prefix+":W2Y_END_SP", 'd') ]
+
+ scan_range = [ caget(prefix+":W1X_START_SP", 'd'), \
+ caget(prefix+":W1X_END_SP", 'd'), \
+ caget(prefix+":W1Y_START_SP", 'd'), \
+ caget(prefix+":W1Y_END_SP", 'd') ]
+
+velocity = abs(scan_range[1]-scan_range[0])*100/2000
+
+print scan_range
+print velocity
+
+scanner = WireScanner(prefix, scan_range = scan_range, cycles=cycles, velocity=None, continuous = True)
+add_device(scanner, True)
+add_device(scanner.motor , True)
+scanner.motor.monitored=True
+
+
+#"m_pos" -> scanner.motor_bs_readback
+m_pos = scanner.motor.readback
+class w_pos(Readable):
+ def read(self):
+ return scanner.get_sel_wire_pos(m_pos.take())
+
+
+
+
+scanner.park(wait=True)
+scanner.set_velocity(velocity)
+scanner.set_selection(WireScanner.W1X)
+scanner.init(wait=True)
+scanner.curr_cycl.write(0)
+scan_complete=False
+cur_cycle = 1.0
+#Scan
+
+
+ scanner.scan() #scanner.waitState(State.Busy, 60000) Not needed as stream filter will make the wait
+
+
+def check_end_scan(record, scan):
+ global scan_complete,cur_cycle
+ print scanner.take()
+ if scanner.take() == "Scan done":
+ print "Done"
+ scan_complete=True
+ scan.abort()
+ record.cancel() #So it won't be saved
+
+try:
+ l=[w_pos()] ; #l.extend(st.getReadables()); l.append(Timestamp())
+ print "Start scan"
+ #mscan (st, l, -1, -1, take_initial = True, after_read = check_end_scan)
+ mscan (m_pos, l, -1, -1, take_initial = True, after_read = check_end_scan)
+ #print "End scan"
+ #tscan([w_pos()] + st.getReadables() + [Timestamp(),], 10, 0.5)
+except:
+ print sys.exc_info()[1]
+ if not scanner.isReady():
+ print "Aborting scan"
+ scanner.abort()
+ if not scan_complete:
+ raise
+finally:
+ print "Finished"
+ #scanner.close()
+ #st.close()
+
diff --git a/script/__Lib/_startup.py b/script/__Lib/_startup.py
new file mode 100644
index 0000000..1feb5f0
--- /dev/null
+++ b/script/__Lib/_startup.py
@@ -0,0 +1,2529 @@
+###################################################################################################
+# Global definitions and built-in functions
+###################################################################################################
+
+import sys
+import time
+import math
+import os.path
+from operator import add, mul, sub, truediv
+from time import sleep
+from array import array
+import jarray
+
+import java.lang.Class as Class
+import java.lang.Object as Object
+import java.beans.PropertyChangeListener
+import java.util.concurrent.Callable
+import java.util.List
+import java.lang.reflect.Array
+import java.lang.Thread
+import java.awt.image.BufferedImage as BufferedImage
+import java.awt.Color as Color
+import java.awt.Dimension as Dimension
+import java.awt.Font as Font
+import org.python.core.PyArray as PyArray
+import org.python.core.PyFunction as PyFunction
+
+import ch.psi.utils.Threading as Threading
+import ch.psi.utils.State as State
+import ch.psi.utils.Convert as Convert
+import ch.psi.utils.Arr as Arr
+import ch.psi.utils.Chrono as Chrono
+import ch.psi.pshell.core.CommandSource as CommandSource
+import ch.psi.pshell.core.ContextAdapter as ContextListener
+import ch.psi.pshell.core.Context
+import ch.psi.pshell.core.InlineDevice as InlineDevice
+import ch.psi.pshell.data.PlotDescriptor as PlotDescriptor
+import ch.psi.pshell.data.Table as Table
+import ch.psi.pshell.device.Device as Device
+import ch.psi.pshell.device.DeviceBase as DeviceBase
+import ch.psi.pshell.device.RegisterBase as RegisterBase
+import ch.psi.pshell.device.ProcessVariableBase as ProcessVariableBase
+import ch.psi.pshell.device.ControlledVariableBase as ControlledVariableBase
+import ch.psi.pshell.device.PositionerBase as PositionerBase
+import ch.psi.pshell.device.MotorBase as MotorBase
+import ch.psi.pshell.device.DiscretePositionerBase as DiscretePositionerBase
+import ch.psi.pshell.device.MotorGroupBase as MotorGroupBase
+import ch.psi.pshell.device.MotorGroupDiscretePositioner as MotorGroupDiscretePositioner
+import ch.psi.pshell.device.ReadonlyRegisterBase as ReadonlyRegisterBase
+import ch.psi.pshell.device.ReadonlyAsyncRegisterBase as ReadonlyAsyncRegisterBase
+import ch.psi.pshell.device.Register as Register
+import ch.psi.pshell.device.RegisterCache as RegisterCache
+import ch.psi.pshell.device.ReadonlyRegister.ReadonlyRegisterArray as ReadonlyRegisterArray
+import ch.psi.pshell.device.ReadonlyRegister.ReadonlyRegisterMatrix as ReadonlyRegisterMatrix
+import ch.psi.pshell.device.DummyPositioner as DummyPositioner
+import ch.psi.pshell.device.DummyMotor as DummyMotor
+import ch.psi.pshell.device.DummyRegister as DummyRegister
+import ch.psi.pshell.device.Timestamp as Timestamp
+import ch.psi.pshell.device.Interlock as Interlock
+import ch.psi.pshell.device.Readable as Readable
+import ch.psi.pshell.device.Readable.ReadableArray as ReadableArray
+import ch.psi.pshell.device.Readable.ReadableMatrix as ReadableMatrix
+import ch.psi.pshell.device.Readable.ReadableCalibratedArray as ReadableCalibratedArray
+import ch.psi.pshell.device.Readable.ReadableCalibratedMatrix as ReadableCalibratedMatrix
+import ch.psi.pshell.device.ArrayCalibration as ArrayCalibration
+import ch.psi.pshell.device.MatrixCalibration as MatrixCalibration
+import ch.psi.pshell.device.Writable as Writable
+import ch.psi.pshell.device.Writable.WritableArray as WritableArray
+import ch.psi.pshell.device.Stoppable as Stoppable
+import ch.psi.pshell.device.Averager as Averager
+import ch.psi.pshell.device.ArrayAverager as ArrayAverager
+import ch.psi.pshell.device.Delta as Delta
+import ch.psi.pshell.device.DeviceAdapter as DeviceListener
+import ch.psi.pshell.device.ReadbackDeviceAdapter as ReadbackDeviceListener
+import ch.psi.pshell.device.MotorAdapter as MotorListener
+import ch.psi.pshell.device.MoveMode as MoveMode
+import ch.psi.pshell.device.SettlingCondition as SettlingCondition
+import ch.psi.pshell.epics.Epics as Epics
+import ch.psi.pshell.epics.EpicsScan as EpicsScan
+import ch.psi.pshell.epics.ChannelSettlingCondition as ChannelSettlingCondition
+import ch.psi.pshell.imaging.Source as Source
+import ch.psi.pshell.imaging.SourceBase as SourceBase
+import ch.psi.pshell.imaging.DirectSource as DirectSource
+import ch.psi.pshell.imaging.RegisterMatrixSource as RegisterMatrixSource
+import ch.psi.pshell.imaging.ImageListener as ImageListener
+import ch.psi.pshell.plot.LinePlotSeries as LinePlotSeries
+import ch.psi.pshell.plot.LinePlotErrorSeries as LinePlotErrorSeries
+import ch.psi.pshell.plot.MatrixPlotSeries as MatrixPlotSeries
+import ch.psi.pshell.scan.ScanBase as ScanBase
+import ch.psi.pshell.scan.LineScan
+import ch.psi.pshell.scan.ContinuousScan
+import ch.psi.pshell.scan.AreaScan
+import ch.psi.pshell.scan.VectorScan
+import ch.psi.pshell.scan.ManualScan
+import ch.psi.pshell.scan.HardwareScan
+import ch.psi.pshell.scan.RegionScan
+import ch.psi.pshell.scan.TimeScan
+import ch.psi.pshell.scan.MonitorScan
+import ch.psi.pshell.scan.BinarySearch
+import ch.psi.pshell.scan.HillClimbingSearch
+import ch.psi.pshell.scan.ScanResult
+import ch.psi.pshell.bs.BsScan
+import ch.psi.pshell.bs.Stream as Stream
+import ch.psi.pshell.scripting.ViewPreference as Preference
+import ch.psi.pshell.scripting.ScriptUtils as ScriptUtils
+
+def get_context():
+ return ch.psi.pshell.core.Context.getInstance()
+
+###################################################################################################
+#Type conversion and checking
+###################################################################################################
+
+def to_array(obj, type = 'o'):
+ """Convert Python list to Java array.
+
+ Args:
+ obj(list): Original data.
+ type(str): array type 'b' = byte, 'h' = short, 'i' = int, 'l' = long, 'f' = float, 'd' = double,
+ 'c' = char, 'z' = boolean, 's' = String, 'o' = Object
+ Returns:
+ Java array.
+
+ """
+ if type[0] == '[':
+ type = type[1:]
+ arrayType = ScriptUtils.getType("["+type)
+
+ if obj is None:
+ return None
+ if isinstance(obj,java.util.List):
+ obj = obj.toArray()
+ if type != 'o':
+ obj = Convert.toPrimitiveArray(obj, ScriptUtils.getType(type))
+ if isinstance(obj,PyArray):
+ if type != 'o':
+ if (Arr.getRank(obj)== 1) and (obj.typecode != type):
+ ret = java.lang.reflect.Array.newInstance(ScriptUtils.getType(type), len(obj))
+ if type == 's':
+ for i in range(len(obj)): ret[i] = str(obj[i])
+ elif type == 'c':
+ for i in range(len(obj)): ret[i] = chr(obj[i])
+ else:
+ for i in range(len(obj)): ret[i] = obj[i]
+ obj = ret
+ if type not in ['o', 's']:
+ obj = Convert.toPrimitiveArray(obj)
+ return obj
+ if is_list(obj):
+ if type=='o' or type== 's':
+ ret = java.lang.reflect.Array.newInstance(ScriptUtils.getType(type), len(obj))
+ for i in range (len(obj)):
+ if is_list(obj[i]):
+ ret[i] = to_array(obj[i],type)
+ elif type == 's':
+ ret[i] = str(obj[i])
+ else:
+ ret[i] = obj[i]
+ return ret
+
+ if len(obj)>0 and is_list(obj[0]):
+ if len(obj[0])>0 and is_list(obj[0][0]):
+ ret = java.lang.reflect.Array.newInstance(arrayType,len(obj),len(obj[0]))
+ for i in range(len(obj)):
+ ret[i]=to_array(obj[i], type)
+ return ret
+ else:
+ ret = java.lang.reflect.Array.newInstance(arrayType,len(obj))
+ for i in range(len(obj)):
+ ret[i]=to_array(obj[i], type)
+ return ret
+ return jarray.array(obj,type)
+ return obj
+
+def to_list(obj):
+ """Convert an object into a Python List.
+
+ Args:
+ obj(tuple or array or ArrayList): Original data.
+ Returns:
+ List.
+
+ """
+ if obj is None:
+ return None
+ if isinstance(obj,tuple) or isinstance(obj,java.util.ArrayList) :
+ return list(obj)
+ #if isinstance(obj,PyArray):
+ # return obj.tolist()
+ if not isinstance(obj,list):
+ return [obj,]
+ return obj
+
+def is_list(obj):
+ return isinstance(obj,tuple) or isinstance(obj,list) or isinstance (obj, java.util.ArrayList)
+
+def is_string(obj):
+ return (type(obj) is str) or (type(obj) is unicode)
+
+
+###################################################################################################
+#Standard scan commands
+###################################################################################################
+
+def on_before_scan_readout(scan, pos):
+ try:
+ if scan.before_read != None:
+ arguments = scan.before_read.func_code.co_argcount
+ if arguments == 0:
+ scan.before_read()
+ elif arguments==1:
+ scan.before_read(pos.tolist())
+ elif arguments==2:
+ scan.before_read(pos.tolist(), scan)
+ except AttributeError:
+ pass
+
+def on_after_scan_readout(scan, record):
+ try:
+ if scan.after_read != None:
+ arguments = scan.after_read.func_code.co_argcount
+ if arguments == 0:
+ scan.after_read()
+ elif arguments==1:
+ scan.after_read(record)
+ elif arguments==2:
+ scan.after_read(record, scan)
+ except AttributeError:
+ pass
+
+def on_before_scan_pass(scan, num_pass):
+ try:
+ if scan.before_pass != None:
+ arguments = scan.before_pass.func_code.co_argcount
+ if arguments == 0:
+ scan.before_pass()
+ elif arguments==1:
+ scan.before_pass(num_pass)
+ elif arguments==2:
+ scan.before_pass(num_pass, scan)
+ except AttributeError:
+ pass
+
+def on_after_scan_pass(scan, num_pass):
+ try:
+ if scan.after_pass != None:
+ arguments = scan.after_pass.func_code.co_argcount
+ if arguments == 0:
+ scan.after_pass()
+ elif arguments==1:
+ scan.after_pass(num_pass)
+ elif arguments==2:
+ scan.after_pass(num_pass, scan)
+ except AttributeError:
+ pass
+
+class LineScan(ch.psi.pshell.scan.LineScan):
+ def onBeforeReadout(self, pos):
+ on_before_scan_readout(self, pos)
+
+ def onAfterReadout(self, record):
+ on_after_scan_readout(self, record)
+
+ def onBeforePass(self, num_pass):
+ on_before_scan_pass(self, num_pass)
+
+ def onAfterPass(self, num_pass):
+ on_after_scan_pass(self, num_pass)
+
+class ContinuousScan(ch.psi.pshell.scan.ContinuousScan):
+ def onBeforeReadout(self, pos):
+ on_before_scan_readout(self, pos)
+
+ def onAfterReadout(self, record):
+ on_after_scan_readout(self, record)
+
+ def onBeforePass(self, num_pass):
+ on_before_scan_pass(self, num_pass)
+
+ def onAfterPass(self, num_pass):
+ on_after_scan_pass(self, num_pass)
+
+class AreaScan(ch.psi.pshell.scan.AreaScan):
+ def onBeforeReadout(self, pos):
+ on_before_scan_readout(self, pos)
+
+ def onAfterReadout(self, record):
+ on_after_scan_readout(self, record)
+
+ def onBeforePass(self, num_pass):
+ on_before_scan_pass(self, num_pass)
+
+ def onAfterPass(self, num_pass):
+ on_after_scan_pass(self, num_pass)
+
+class RegionScan(ch.psi.pshell.scan.RegionScan):
+ def onBeforeReadout(self, pos):
+ on_before_scan_readout(self, pos)
+
+ def onAfterReadout(self, record):
+ on_after_scan_readout(self, record)
+
+ def onBeforePass(self, num_pass):
+ on_before_scan_pass(self, num_pass)
+
+ def onAfterPass(self, num_pass):
+ on_after_scan_pass(self, num_pass)
+
+class VectorScan(ch.psi.pshell.scan.VectorScan):
+ def onBeforeReadout(self, pos):
+ on_before_scan_readout(self, pos)
+
+ def onAfterReadout(self, record):
+ on_after_scan_readout(self, record)
+
+ def onBeforePass(self, num_pass):
+ on_before_scan_pass(self, num_pass)
+
+ def onAfterPass(self, num_pass):
+ on_after_scan_pass(self, num_pass)
+
+class ContinuousScan(ch.psi.pshell.scan.ContinuousScan):
+ def onBeforeReadout(self, pos):
+ on_before_scan_readout(self, pos)
+
+ def onAfterReadout(self, record):
+ on_after_scan_readout(self, record)
+
+ def onBeforePass(self, num_pass):
+ on_before_scan_pass(self, num_pass)
+
+ def onAfterPass(self, num_pass):
+ on_after_scan_pass(self, num_pass)
+
+class TimeScan(ch.psi.pshell.scan.TimeScan):
+ def onBeforeReadout(self, pos):
+ on_before_scan_readout(self, pos)
+
+ def onAfterReadout(self, record):
+ on_after_scan_readout(self, record)
+
+ def onBeforePass(self, num_pass):
+ on_before_scan_pass(self, num_pass)
+
+ def onAfterPass(self, num_pass):
+ on_after_scan_pass(self, num_pass)
+
+class MonitorScan(ch.psi.pshell.scan.MonitorScan):
+ def onBeforeReadout(self, pos):
+ on_before_scan_readout(self, pos)
+
+ def onAfterReadout(self, record):
+ on_after_scan_readout(self, record)
+
+ def onBeforePass(self, num_pass):
+ on_before_scan_pass(self, num_pass)
+
+ def onAfterPass(self, num_pass):
+ on_after_scan_pass(self, num_pass)
+
+class BsScan(ch.psi.pshell.bs.BsScan):
+ def onBeforeReadout(self, pos):
+ on_before_scan_readout(self, pos)
+
+ def onAfterReadout(self, record):
+ on_after_scan_readout(self, record)
+
+ def onBeforePass(self, num_pass):
+ on_before_scan_pass(self, num_pass)
+
+ def onAfterPass(self, num_pass):
+ on_after_scan_pass(self, num_pass)
+
+class ManualScan (ch.psi.pshell.scan.ManualScan):
+ def __init__(self, writables, readables, start = None, end = None, steps = None, relative = False, dimensions = None):
+ ch.psi.pshell.scan.ManualScan.__init__(self, writables, readables, start, end, steps, relative)
+ self._dimensions = dimensions
+
+ def append(self,setpoints, positions, values, timestamps=None):
+ ch.psi.pshell.scan.ManualScan.append(self, to_array(setpoints), to_array(positions), to_array(values), None if (timestamps is None) else to_array(timestamps))
+
+ def getDimensions(self):
+ if self._dimensions == None:
+ return ch.psi.pshell.scan.ManualScan.getDimensions(self)
+ else:
+ return self._dimensions
+
+class BinarySearch(ch.psi.pshell.scan.BinarySearch):
+ def onBeforeReadout(self, pos):
+ on_before_scan_readout(self, pos)
+
+ def onAfterReadout(self, record):
+ on_after_scan_readout(self, record)
+
+class HillClimbingSearch(ch.psi.pshell.scan.HillClimbingSearch):
+ def onBeforeReadout(self, pos):
+ on_before_scan_readout(self, pos)
+
+ def onAfterReadout(self, record):
+ on_after_scan_readout(self, record)
+
+def processScanPars(scan, pars):
+ scan.before_read = pars.pop("before_read",None)
+ scan.after_read = pars.pop("after_read",None)
+ scan.before_pass = pars.pop("before_pass",None)
+ scan.after_pass = pars.pop("after_pass",None)
+ scan.setPlotTitle(pars.pop("title",None))
+ scan.setHidden(pars.pop("hidden",False))
+ scan.setSettleTimeout (pars.pop("settle_timeout",ScanBase.getScansSettleTimeout()))
+ scan.setUseWritableReadback (pars.pop("use_readback",ScanBase.getScansUseWritableReadback()))
+ scan.setInitialMove(pars.pop("initial_move",ScanBase.getScansTriggerInitialMove()))
+ scan.setParallelPositioning(pars.pop("parallel_positioning",ScanBase.getScansParallelPositioning()))
+ scan.setAbortOnReadableError(pars.pop("abort_on_error",ScanBase.getAbortScansOnReadableError()))
+ scan.setRestorePosition (pars.pop("restore_position",ScanBase.getRestorePositionOnRelativeScans()))
+ scan.setCheckPositions(pars.pop("check_positions",ScanBase.getScansCheckPositions()))
+
+
+ get_context().setCommandPars(scan, pars)
+
+def lscan(writables, readables, start, end, steps, latency=0.0, relative=False, passes=1, zigzag=False, **pars):
+ """Line Scan: positioners change together, linearly from start to end positions.
+
+ Args:
+ writables(list of Writable): Positioners set on each step.
+ readables(list of Readable): Sensors to be sampled on each step.
+ start(list of float): start positions of writables.
+ end(list of float): final positions of writables.
+ steps(int or float or list of float): number of scan steps (int) or step size (float).
+ relative (bool, optional): if true, start and end positions are relative to
+ current at start of the scan
+ latency(float, optional): settling time for each step before readout, defaults to 0.0.
+ passes(int, optional): number of passes
+ zigzag(bool, optional): if true writables invert direction on each pass.
+ pars(keyworded variable length arguments, optional): scan optional named arguments:
+ - title(str, optional): plotting window name.
+ - hidden(bool, optional): if true generates no effects on user interface.
+ - before_read (function, optional): callback on each step, before sampling. Arguments: positions, scan
+ - after_read (function, optional): callback on each step, after sampling. Arguments: record, scan.
+ - before_pass (function, optional): callback before each scan pass execution. Arguments: pass_num, scan.
+ - after_pass (function, optional): callback after each scan pass execution. Arguments: pass_num, scan.
+ - settle_timeout(int, optional): timeout for each positioner get to position. Default (-1) waits forever.
+ - initial_move (bool, optional): if true (default) perform move to initial position prior to scan start.
+ - parallel_positioning (bool, optional): if true (default) all positioners are set in parallel.
+ - abort_on_error (bool, optional): if true then aborts scan in sensor failures. Default is false.
+ - restore_position (bool, optional): if true (default) then restore initial position after relative scans.
+ - check_positions (bool, optional): if true (default) verifies if in correct positions after move finishes.
+ - Aditional arguments defined by set_exec_pars.
+
+ Returns:
+ ScanResult object.
+
+ """
+ latency_ms=int(latency*1000)
+ writables=to_list(string_to_obj(writables))
+ readables=to_list(string_to_obj(readables))
+ start=to_list(start)
+ end=to_list(end)
+ if type(steps) is float or is_list(steps):
+ steps = to_list(steps)
+ scan = LineScan(writables,readables, start, end , steps, relative, latency_ms, int(passes), zigzag)
+ processScanPars(scan, pars)
+ scan.start()
+ return scan.getResult()
+
+def vscan(writables, readables, vector, line = False, latency=0.0, relative=False, passes=1, zigzag=False, **pars):
+ """Vector Scan: positioners change following values provided in a vector.
+
+ Args:
+ writables(list of Writable): Positioners set on each step.
+ readables(list of Readable): Sensors to be sampled on each step.
+ vector(list of list of float): table of positioner values.
+ line (bool, optional): if true, processs as line scan (1d)
+ relative (bool, optional): if true, start and end positions are relative to current at
+ start of the scan
+ latency(float, optional): settling time for each step before readout, defaults to 0.0.
+ passes(int, optional): number of passes
+ zigzag(bool, optional): if true writables invert direction on each pass.
+ pars(keyworded variable length arguments, optional): scan optional named arguments:
+ - title(str, optional): plotting window name.
+ - before_read (function, optional): callback on each step, before sampling. Arguments: positions, scan
+ - after_read (function, optional): callback on each step, after sampling. Arguments: record, scan.
+ - before_pass (function, optional): callback before each scan pass execution. Arguments: pass_num, scan.
+ - after_pass (function, optional): callback after each scan pass execution. Arguments: pass_num, scan.
+ - settle_timeout(int, optional): timeout for each positioner get to position. Default (-1) waits forever.
+ - initial_move (bool, optional): if true (default) perform move to initial position prior to scan start.
+ - parallel_positioning (bool, optional): if true (default) all positioners are set in parallel.
+ - abort_on_error (bool, optional): if true then aborts scan in sensor failures. Default is false.
+ - restore_position (bool, optional): if true (default) then restore initial position after relative scans.
+ - check_positions (bool, optional): if true (default) verifies if in correct positions after move finishes.
+ - Aditional arguments defined by set_exec_pars.
+
+ Returns:
+ ScanResult object.
+
+ """
+ latency_ms=int(latency*1000)
+ writables=to_list(string_to_obj(writables))
+ readables=to_list(string_to_obj(readables))
+ if len(vector) == 0:
+ vector.append([])
+ elif (not is_list(vector[0])) and (not isinstance(vector[0],PyArray)):
+ vector = [[x,] for x in vector]
+ vector = to_array(vector, 'd')
+ scan = VectorScan(writables,readables, vector, line, relative, latency_ms, int(passes), zigzag)
+ processScanPars(scan, pars)
+ scan.start()
+ return scan.getResult()
+
+def ascan(writables, readables, start, end, steps, latency=0.0, relative=False, passes=1, zigzag=False, **pars):
+ """Area Scan: multi-dimentional scan, each positioner is a dimention.
+
+ Args:
+ writables(list of Writable): Positioners set on each step.
+ readables(list of Readable): Sensors to be sampled on each step.
+ start(list of float): start positions of writables.
+ end(list of float): final positions of writables.
+ steps(list of int or list of float): number of scan steps (int) or step size (float).
+ latency(float, optional): settling time for each step before readout, defaults to 0.0.
+ relative (bool, optional): if true, start and end positions are relative to current at
+ start of the scan
+ passes(int, optional): number of passes
+ zigzag (bool, optional): if true writables invert direction on each row.
+ pars(keyworded variable length arguments, optional): scan optional named arguments:
+ - title(str, optional): plotting window name.
+ - before_read (function, optional): callback on each step, before sampling. Arguments: positions, scan
+ - after_read (function, optional): callback on each step, after sampling. Arguments: record, scan.
+ - before_pass (function, optional): callback before each scan pass execution. Arguments: pass_num, scan.
+ - after_pass (function, optional): callback after each scan pass execution. Arguments: pass_num, scan.
+ - settle_timeout(int, optional): timeout for each positioner get to position. Default (-1) waits forever.
+ - initial_move (bool, optional): if true (default) perform move to initial position prior to scan start.
+ - parallel_positioning (bool, optional): if true (default) all positioners are set in parallel.
+ - abort_on_error (bool, optional): if true then aborts scan in sensor failures. Default is false.
+ - restore_position (bool, optional): if true (default) then restore initial position after relative scans.
+ - check_positions (bool, optional): if true (default) verifies if in correct positions after move finishes.
+ - Aditional arguments defined by set_exec_pars.
+
+ Returns:
+ ScanResult object.
+
+ """
+ latency_ms=int(latency*1000)
+ writables=to_list(string_to_obj(writables))
+ readables=to_list(string_to_obj(readables))
+ start=to_list(start)
+ end=to_list(end)
+ if is_list(steps):
+ steps = to_list(steps)
+ scan = AreaScan(writables,readables, start, end , steps, relative, latency_ms, int(passes), zigzag)
+ processScanPars(scan, pars)
+ scan.start()
+ return scan.getResult()
+
+
+def rscan(writable, readables, regions, latency=0.0, relative=False, passes=1, zigzag=False, **pars):
+ """Region Scan: positioner scanned linearly, from start to end positions, in multiple regions.
+
+ Args:
+ writable(Writable): Positioner set on each step, for each region.
+ readables(list of Readable): Sensors to be sampled on each step.
+ regions (list of tuples (float,float, int) or (float,float, float)): each tuple define a scan region
+ (start, stop, steps) or (start, stop, step_size)
+ relative (bool, optional): if true, start and end positions are relative to
+ current at start of the scan
+ latency(float, optional): settling time for each step before readout, defaults to 0.0.
+ passes(int, optional): number of passes
+ zigzag(bool, optional): if true writable invert direction on each pass.
+ pars(keyworded variable length arguments, optional): scan optional named arguments:
+ - title(str, optional): plotting window name.
+ - before_read (function, optional): callback on each step, before sampling. Arguments: positions, scan
+ - after_read (function, optional): callback on each step, after sampling. Arguments: record, scan.
+ - before_pass (function, optional): callback before each scan pass execution. Arguments: pass_num, scan.
+ - after_pass (function, optional): callback after each scan pass execution. Arguments: pass_num, scan.
+ - settle_timeout(int, optional): timeout for each positioner get to position. Default (-1) waits forever.
+ - initial_move (bool, optional): if true (default) perform move to initial position prior to scan start.
+ - parallel_positioning (bool, optional): if true (default) all positioners are set in parallel.
+ - abort_on_error (bool, optional): if true then aborts scan in sensor failures. Default is false.
+ - restore_position (bool, optional): if true (default) then restore initial position after relative scans.
+ - check_positions (bool, optional): if true (default) verifies if in correct positions after move finishes.
+ - Aditional arguments defined by set_exec_pars.
+
+ Returns:
+ ScanResult object.
+
+ """
+ start=[]
+ end=[]
+ steps=[]
+ for region in regions:
+ start.append(region[0])
+ end.append(region[1])
+ steps.append(region[2])
+ latency_ms=int(latency*1000)
+ writable=string_to_obj(writable)
+ readables=to_list(string_to_obj(readables))
+ start=to_list(start)
+ end=to_list(end)
+ steps = to_list(steps)
+ scan = RegionScan(writable,readables, start, end , steps, relative, latency_ms, int(passes), zigzag)
+ processScanPars(scan, pars)
+ scan.start()
+ return scan.getResult()
+
+def cscan(writables, readables, start, end, steps, latency=0.0, time=None, relative=False, passes=1, zigzag=False, **pars):
+ """Continuous Scan: positioner change continuously from start to end position and readables are sampled on the fly.
+
+ Args:
+ writable(Speedable or list of Motor): A positioner with a getSpeed method or
+ a list of motors.
+ readables(list of Readable): Sensors to be sampled on each step.
+ start(float or list of float): start positions of writables.
+ end(float or list of float): final positions of writabless.
+ steps(int or float or list of float): number of scan steps (int) or step size (float).
+ latency(float, optional): sleep time in each step before readout, defaults to 0.0.
+ time (float, seconds): if not None then writables is Motor array and speeds are
+ set according to time.
+ relative (bool, optional): if true, start and end positions are relative to
+ current at start of the scan
+ passes(int, optional): number of passes
+ pars(keyworded variable length arguments, optional): scan optional named arguments:
+ - title(str, optional): plotting window name.
+ - before_read (function, optional): callback on each step, before sampling. Arguments: positions, scan
+ - after_read (function, optional): callback on each step, after sampling. Arguments: record, scan.
+ - before_pass (function, optional): callback before each scan pass execution. Arguments: pass_num, scan.
+ - after_pass (function, optional): callback after each scan pass execution. Arguments: pass_num, scan.
+ - abort_on_error (bool, optional): if true then aborts scan in sensor failures. Default is false.
+ - restore_position (bool, optional): if true (default) then restore initial position after relative scans.
+ - Aditional arguments defined by set_exec_pars.
+
+ Returns:
+ ScanResult object.
+
+ """
+ latency_ms=int(latency*1000)
+ readables=to_list(string_to_obj(readables))
+ writables=to_list(string_to_obj(writables))
+ start=to_list(start)
+ end=to_list(end)
+ #A single Writable with fixed speed
+ if time is None:
+ if is_list(steps): steps=steps[0]
+ scan = ContinuousScan(writables[0],readables, start[0], end[0] , steps, relative, latency_ms, int(passes), zigzag)
+ #A set of Writables with speed configurable
+ else:
+ if type(steps) is float or is_list(steps):
+ steps = to_list(steps)
+ scan = ContinuousScan(writables,readables, start, end , steps, time, relative, latency_ms, int(passes), zigzag)
+
+ processScanPars(scan, pars)
+ scan.start()
+ return scan.getResult()
+
+def hscan(config, writable, readables, start, end, steps, passes=1, zigzag=False, **pars):
+ """Hardware Scan: values sampled by external hardware and received asynchronously.
+
+ Args:
+ config(dict): Configuration of the hardware scan. The "class" key provides the implementation class.
+ Other keys are implementation specific.
+ writable(Writable): A positioner appropriated to the hardware scan type.
+ readables(list of Readable): Sensors appropriated to the hardware scan type.
+ start(float): start positions of writable.
+ end(float): final positions of writables.
+ steps(int or float): number of scan steps (int) or step size (float).
+ passes(int, optional): number of passes
+ pars(keyworded variable length arguments, optional): scan optional named arguments:
+ - title(str, optional): plotting window name.
+ - after_read (function, optional): callback on each step, after sampling. Arguments: record, scan.
+ - before_pass (function, optional): callback before each scan pass execution. Arguments: pass_num, scan.
+ - after_pass (function, optional): callback after each scan pass execution. Arguments: pass_num, scan.
+ - abort_on_error (bool, optional): if true then aborts scan in sensor failures. Default is false.
+ - Aditional arguments defined by set_exec_pars.
+
+ Returns:
+ ScanResult object.
+
+ """
+ cls = Class.forName(config["class"])
+ class HardwareScan(cls):
+ def __init__(self, config, writable, readables, start, end, stepSize, passes, zigzag):
+ cls.__init__(self, config, writable, readables, start, end, stepSize, passes, zigzag)
+ def onAfterReadout(self, record):
+ on_after_scan_readout(self, record)
+ def onBeforePass(self, num_pass):
+ on_before_scan_pass(self, num_pass)
+ def onAfterPass(self, num_pass):
+ on_after_scan_pass(self, num_pass)
+
+ readables=to_list(string_to_obj(readables))
+ scan = HardwareScan(config, writable,readables, start, end , steps, int(passes), zigzag)
+ processScanPars(scan, pars)
+ scan.start()
+ return scan.getResult()
+
+def bscan(stream, records, timeout = None, passes=1, **pars):
+ """BS Scan: records all values in a beam synchronous stream.
+
+ Args:
+ stream(Stream): stream object or list of chanel names to build stream from
+ records(int): number of records to store
+ timeout(float, optional): maximum scan time in seconds.
+ passes(int, optional): number of passes
+ pars(keyworded variable length arguments, optional): scan optional named arguments:
+ - title(str, optional): plotting window name.
+ - before_read (function, optional): callback on each step, before sampling. Arguments: positions, scan
+ - after_read (function, optional): callback on each step, after sampling. Arguments: record, scan.
+ - before_pass (function, optional): callback before each scan pass execution. Arguments: pass_num, scan.
+ - after_pass (function, optional): callback after each scan pass execution. Arguments: pass_num, scan.
+ - Aditional arguments defined by set_exec_pars.
+
+ Returns:
+ ScanResult object.
+
+ """
+ timeout_ms=int(timeout*1000) if ((timeout is not None) and (timeout>=0)) else -1
+ if not is_list(stream):
+ stream=string_to_obj(stream)
+ scan = BsScan(stream,int(records), timeout_ms, int(passes))
+ processScanPars(scan, pars)
+ scan.start()
+ return scan.getResult()
+
+def tscan(readables, points, interval, passes=1, **pars):
+ """Time Scan: sensors are sampled in fixed time intervals.
+
+ Args:
+ readables(list of Readable): Sensors to be sampled on each step.
+ points(int): number of samples.
+ interval(float): time interval between readouts. Minimum temporization is 0.001s
+ passes(int, optional): number of passes
+ pars(keyworded variable length arguments, optional): scan optional named arguments:
+ - title(str, optional): plotting window name.
+ - before_read (function, optional): callback on each step, before sampling. Arguments: positions, scan
+ - after_read (function, optional): callback on each step, after sampling. Arguments: record, scan.
+ - before_pass (function, optional): callback before each scan pass execution. Arguments: pass_num, scan.
+ - after_pass (function, optional): callback after each scan pass execution. Arguments: pass_num, scan.
+ - abort_on_error (bool, optional): if true then aborts scan in sensor failures. Default is false.
+ - Aditional arguments defined by set_exec_pars.
+
+ Returns:
+ ScanResult object.
+
+ """
+ interval= max(interval, 0.001) #Minimum temporization is 1ms
+ interval_ms=int(interval*1000)
+ readables=to_list(string_to_obj(readables))
+ scan = TimeScan(readables, points, interval_ms, int(passes))
+ processScanPars(scan, pars)
+ scan.start()
+ return scan.getResult()
+
+def mscan(trigger, readables, points, timeout = None, async=True, take_initial=False, passes=1, **pars):
+ """Monitor Scan: sensors are sampled when received change event of the trigger device.
+
+ Args:
+ trigger(Device or list of Device): Source of the sampling triggering.
+ readables(list of Readable): Sensors to be sampled on each step.
+ If trigger has cache and is included in readables, it is not read
+ for each step, but the change event value is used.
+ points(int): number of samples.
+ timeout(float, optional): maximum scan time in seconds.
+ async(bool, optional): if True then records are sampled and stored on event change callback. Enforce
+ reading only cached values of sensors.
+ If False, the scan execution loop waits for trigger cache update. Do not make
+ cache only access, but may loose change events.
+ take_initial(bool, optional): if True include current values as first record (before first trigger).
+ passes(int, optional): number of passes
+ pars(keyworded variable length arguments, optional): scan optional named arguments:
+ - title(str, optional): plotting window name.
+ - before_read (function, optional): callback on each step, before sampling. Arguments: positions, scan
+ - after_read (function, optional): callback on each step, after sampling. Arguments: record, scan.
+ - before_pass (function, optional): callback before each scan pass execution. Arguments: pass_num, scan.
+ - after_pass (function, optional): callback after each scan pass execution. Arguments: pass_num, scan.
+ - abort_on_error (bool, optional): if true then aborts scan in sensor failures. Default is false.
+ - Aditional arguments defined by set_exec_pars.
+
+ Returns:
+ ScanResult object.
+
+ """
+ timeout_ms=int(timeout*1000) if ((timeout is not None) and (timeout>=0)) else -1
+ trigger = string_to_obj(trigger)
+ readables=to_list(string_to_obj(readables))
+ scan = MonitorScan(trigger, readables, points, timeout_ms, async, take_initial, int(passes))
+ processScanPars(scan, pars)
+ scan.start()
+ return scan.getResult()
+
+def escan(name, **pars):
+ """Epics Scan: execute an Epics Scan Record.
+
+ Args:
+ name(str): Name of scan record.
+ title(str, optional): plotting window name.
+ pars(keyworded variable length arguments, optional): scan optional named arguments:
+ - title(str, optional): plotting window name.
+ - Aditional arguments defined by set_exec_pars.
+
+ Returns:
+ ScanResult object.
+
+ """
+ scan = EpicsScan(name)
+ processScanPars(scan, pars)
+ scan.start()
+ return scan.getResult()
+
+
+def bsearch(writables, readable, start, end, steps, maximum = True, strategy = "Normal", latency=0.0, relative=False, **pars):
+ """Binary search: searches writables in a binary search fashion to find a local maximum for the readable.
+
+ Args:
+ writables(list of Writable): Positioners set on each step.
+ readable(Readable): Sensor to be sampled.
+ start(list of float): start positions of writables.
+ end(list of float): final positions of writables.
+ steps(float or list of float): resolution of search for each writable.
+ maximum (bool , optional): if True (default) search maximum, otherwise minimum.
+ strategy (str , optional): "Normal": starts search midway to scan range and advance in the best direction.
+ Uses orthogonal neighborhood (4-neighborhood for 2d)
+ "Boundary": starts search on scan range.
+ "FullNeighborhood": Uses complete neighborhood (8-neighborhood for 2d)
+
+ latency(float, optional): settling time for each step before readout, defaults to 0.0.
+ relative (bool, optional): if true, start and end positions are relative to current at
+ start of the scan
+ pars(keyworded variable length arguments, optional): scan optional named arguments:
+ - title(str, optional): plotting window name.
+ - before_read (function, optional): callback on each step, before sampling. Arguments: positions, scan
+ - after_read (function, optional): callback on each step, after sampling. Arguments: record, scan.
+ - settle_timeout(int, optional): timeout for each positioner get to position. Default (-1) waits forever.
+ - parallel_positioning (bool, optional): if true (default) all positioners are set in parallel.
+ - abort_on_error (bool, optional): if true then aborts scan in sensor failures. Default is false.
+ - restore_position (bool, optional): if true (default) then restore initial position after relative scans.
+ - check_positions (bool, optional): if true (default) verifies if in correct positions after move finishes.
+ - Aditional arguments defined by set_exec_pars.
+
+ Returns:
+ SearchResult object.
+
+ """
+ latency_ms=int(latency*1000)
+ writables=to_list(string_to_obj(writables))
+ readable=string_to_obj(readable)
+ start=to_list(start)
+ end=to_list(end)
+ steps = to_list(steps)
+ strategy = BinarySearch.Strategy.valueOf(strategy)
+ scan = BinarySearch(writables,readable, start, end , steps, maximum, strategy, relative, latency_ms)
+ processScanPars(scan, pars)
+ scan.start()
+ return scan.getResult()
+
+def hsearch(writables, readable, range_min, range_max, initial_step, resolution, filter=1, maximum=True, latency=0.0, relative=False, **pars):
+ """Hill Climbing search: searches writables in decreasing steps to find a local maximum for the readable.
+ Args:
+ writables(list of Writable): Positioners set on each step.
+ readable(Readable): Sensor to be sampled.
+ range_min(list of float): minimum positions of writables.
+ range_max(list of float): maximum positions of writables.
+ initial_step(float or list of float):initial step size for for each writable.
+ resolution(float or list of float): resolution of search for each writable (minimum step size).
+ filter(int): number of aditional steps to filter noise
+ maximum (bool , optional): if True (default) search maximum, otherwise minimum.
+ latency(float, optional): settling time for each step before readout, defaults to 0.0.
+ relative (bool, optional): if true, range_min and range_max positions are relative to current at
+ start of the scan
+ pars(keyworded variable length arguments, optional): scan optional named arguments:
+ - title(str, optional): plotting window name.
+ - before_read (function, optional): callback on each step, before sampling. Arguments: positions, scan
+ - after_read (function, optional): callback on each step, after sampling. Arguments: record, scan.
+ - settle_timeout(int, optional): timeout for each positioner get to position. Default (-1) waits forever.
+ - parallel_positioning (bool, optional): if true (default) all positioners are set in parallel.
+ - abort_on_error (bool, optional): if true then aborts scan in sensor failures. Default is false.
+ - restore_position (bool, optional): if true (default) then restore initial position after relative scans.
+ - check_positions (bool, optional): if true (default) verifies if in correct positions after move finishes.
+ - Aditional arguments defined by set_exec_pars.
+
+ Returns:
+ SearchResult object.
+
+ """
+ latency_ms=int(latency*1000)
+ writables=to_list(string_to_obj(writables))
+ readable=string_to_obj(readable)
+ range_min=to_list(range_min)
+ range_max=to_list(range_max)
+ initial_step = to_list(initial_step)
+ resolution = to_list(resolution)
+ scan = HillClimbingSearch(writables,readable, range_min, range_max , initial_step, resolution, filter, maximum, relative, latency_ms)
+ processScanPars(scan, pars)
+ scan.start()
+ return scan.getResult()
+
+
+###################################################################################################
+#Data plotting
+###################################################################################################
+
+def plot(data, name = None, xdata = None, ydata=None, title=None):
+ """Request one or multiple plots of user data (1d, 2d or 3d).
+
+ Args:
+ data: array or list of values. For multiple plots, array of arrays or lists of values.
+ name(str or list of str, optional): plot name or list of names (if multiple plots).
+ xdata: array or list of values. For multiple plots, array of arrays or lists of values.
+ ydata: array or list of values. For multiple plots, array of arrays or lists of values.
+ title(str, optional): plotting window name.
+
+ Returns:
+ ArrayList of Plot objects.
+
+ """
+ if isinstance(data, ch.psi.pshell.data.Table):
+ if is_list(xdata):
+ xdata = to_array(xdata, 'd')
+ return get_context().plot(data,xdata,name,title)
+
+ if isinstance(data, ch.psi.pshell.scan.ScanResult):
+ return get_context().plot(data,title)
+
+ if (name is not None) and is_list(name):
+ if len(name)==0:
+ name=None;
+ else:
+ if (data==None):
+ data = []
+ for n in name:
+ data.append([])
+ plots = java.lang.reflect.Array.newInstance(Class.forName("ch.psi.pshell.data.PlotDescriptor"), len(data))
+ for i in range (len(data)):
+ plotName = None if (name is None) else name[i]
+ x = xdata
+ if is_list(x) and len(x)>0 and (is_list(x[i]) or isinstance(x[i] , java.util.List) or isinstance(x[i],PyArray)):
+ x = x[i]
+ y = ydata
+ if is_list(y) and len(y)>0 and (is_list(y[i]) or isinstance(y[i] , java.util.List) or isinstance(y[i],PyArray)):
+ y = y[i]
+ plots[i] = PlotDescriptor(plotName , to_array(data[i], 'd'), to_array(x, 'd'), to_array(y, 'd'))
+ return get_context().plot(plots,title)
+ else:
+ plot = PlotDescriptor(name, to_array(data, 'd'), to_array(xdata, 'd'), to_array(ydata, 'd'))
+ return get_context().plot(plot,title)
+
+def get_plots(title=None):
+ """Return all current plots in the plotting window given by 'title'.
+
+ Args:
+ title(str, optional): plotting window name.
+
+ Returns:
+ ArrayList of Plot objects.
+
+ """
+ return get_context().getPlots(title)
+
+def get_plot_snapshots(title = None, file_type = "png", size = None, temp_path = get_context().setup.getContextPath()):
+ """Returns list with file names of plots snapshots from a plotting context.
+
+ Args:
+ title(str, optional): plotting window name.
+ file_type(str, optional): "png", "jpg", "bmp" or "gif"
+ size(array, optional): [width, height]
+ temp_path(str, optional): path where the files will be generated.
+
+ Returns:
+ list of strings
+
+ """
+ time.sleep(0.1) #Give some time to plot to be finished - it is not sync with acquisition
+ ret = []
+ if size != None:
+ size = Dimension(size[0], size[1])
+ plots = get_plots(title)
+ for i in range(len(plots)):
+ p = plots[i]
+ name = p.getTitle()
+ if name is None or name == "":
+ name = str(i)
+ file_name = os.path.abspath(temp_path + "/" + name + "." + file_type)
+ p.saveSnapshot(file_name , file_type, size)
+ ret.append(file_name)
+ return ret
+
+
+###################################################################################################
+#Data access functions
+###################################################################################################
+
+def load_data(path, index=0, shape=None):
+ """Read data from the current persistence context or from data files.
+
+ Args:
+ path(str): Path to group or dataset relative to the persistence context root.
+ If in the format 'root|path' then read from path given by 'root'.
+ index(int or listr, optional):
+ if integer, data depth (used for 3D datasets returning a 2d matrix)
+ If a list, specifies the full coordinate for multidimensional datasets.
+ shape(list, optional): only valid if index is a list, provides the shape of the data array.
+ In this case return a flattened a one-dimensional array.
+
+ Returns:
+ Data array
+
+ """
+ if index is not None and is_list(index):
+ slice = get_context().dataManager.getData(path, index, shape)
+ else:
+ slice = get_context().dataManager.getData(path, index)
+ return slice.sliceData
+
+def get_attributes(path):
+ """Get the attributes from the current persistence context or from data files.
+
+ Args:
+ path(str): Path to group or dataset relative to the current persistence context root.
+ If in the format 'root|path' then read from path given by 'root'.
+ Returns:
+ Dictionary
+
+ """
+ return get_context().dataManager.getAttributes(path)
+
+def save_dataset(path, data, type='d'):
+ """Save data into a dataset within the current persistence context.
+
+ Args:
+ path(str): Path to dataset relative to the current persistence context root.
+ type(str, optional): array type 'b' = byte, 'h' = short, 'i' = int, 'l' = long, 'f' = float,
+ 'd' = double, 'c' = char, 's' = String, 'o' = Object
+ default: 'd' (convert data to array of doubles)
+ data (array or list): data to be saved
+ Returns:
+ Dictionary
+
+ """
+ data = to_array(data, type)
+ get_context().dataManager.setDataset(path,data)
+
+def create_group(path):
+ """Create an empty dataset within the current persistence context.
+
+ Args:
+ path(str): Path to group relative to the current persistence context root.
+ Returns:
+ None
+
+ """
+ get_context().dataManager.createGroup(path)
+
+def create_dataset(path, type, unsigned=False, dimensions=None):
+ """Create an empty dataset within the current persistence context.
+
+ Args:
+ path(str): Path to dataset relative to the current persistence context root.
+ type(str): array type 'b' = byte, 'h' = short, 'i' = int, 'l' = long, 'f' = float,
+ 'd' = double, 'c' = char, 's' = String, 'o' = Object
+ unsigned(boolean, optional): create a dataset of unsigned type.
+ dimensions(tuple of int, optional): a 0 value means variable length in that dimension.
+ Returns:
+ None
+
+ """
+ get_context().dataManager.createDataset(path, ScriptUtils.getType(type), unsigned, dimensions)
+
+def create_table(path, names, types=None, lengths=None):
+ """Create an empty table (dataset of compound type) within the current persistence context.
+
+ Args:
+ path(str): Path to dataset relative to the current persistence context root.
+ names(list of strings): name of each column
+ types(array of str): 'b' = byte, 'h' = short, 'i' = int, 'l' = long, 'f' = float,
+ 'd' = double, 'c' = char, 's' = String, 'o' = Object
+ Note:A '[' prefix on type name indicates an array type.
+ lengths(list of int): the array length for each columns(0 for scalar types).
+ Returns:
+ None
+
+ """
+ type_classes = []
+ if (types is not None):
+ for i in range (len(types)):
+ type_classes.append(ScriptUtils.getType(types[i]))
+ get_context().dataManager.createDataset(path, names, type_classes, lengths)
+
+def append_dataset(path, data, index=None, type='d', shape=None):
+ """Append data to dataset.
+
+ Args:
+ path(str): Path to dataset relative to the current persistence context root.
+ data(number or array or list): name of each column.
+ index(int or list, optional): if set then add the data in a specific position in the dataset.
+ If integer is the index in an array (data must be 1 order lower than dataset)
+ If a list, specifies the full coordinate for multidimensional datasets.
+ type(str, optional): array type 'b' = byte, 'h' = short, 'i' = int, 'l' = long, 'f' = float,
+ 'd' = double, 'c' = char, 's' = String, 'o' = Object
+ default: 'd' (convert data to array of doubles)
+ shape(list, optional): only valid if index is a list, provides the shape of the data array.
+ In this case data must be a flattened one-dimensional array.
+ Returns:
+ None
+
+ """
+ data = to_array(data, type)
+ if index is None:
+ get_context().dataManager.appendItem(path, data)
+ else:
+ if is_list(index):
+ if shape is None:
+ shape = [len(index)]
+ get_context().dataManager.setItem(path, data, index, shape)
+ else:
+ get_context().dataManager.setItem(path, data, index)
+
+def append_table(path, data):
+ """Append data to a table (dataset of compound type)
+
+ Args:
+ path(str): Path to dataset relative to the current persistence context root.
+ data(list): List of valus for each column of the table. Array types can be expressed as lists.
+ Returns:
+ None
+
+ """
+ if is_list(data):
+ arr = java.lang.reflect.Array.newInstance(Class.forName("java.lang.Object"),len(data))
+ for i in range (len(data)):
+ if is_list(data[i]):
+ arr[i] = to_array(data[i], 'd')
+ else:
+ arr[i] = data[i]
+ data=arr
+ get_context().dataManager.appendItem(path, data)
+
+def flush_data():
+ """Flush all data files immediately.
+
+ Args:
+ None
+ Returns:
+ None
+ """
+ get_context().dataManager.flush()
+
+def set_attribute(path, name, value, unsigned = False):
+ """Set an attribute to a group or dataset.
+
+ Args:
+ path(str): Path to dataset relative to the current persistence context root.
+ name(str): name of the atttribute
+ value(Object): the attribute value
+ unsigned(bool, optional): if applies, indicate if value is unsigned.
+ Returns:
+ None
+ """
+ if is_list(value):
+ value = Convert.toStringArray(to_array(value))
+ get_context().dataManager.setAttribute(path, name, value, unsigned)
+
+def log(log, data_file=True):
+ """Writes a log to the system log and data context - if there is an ongoing scan or script execution.
+
+ Args:
+ log(str): Log string.
+ data_file(bool, optional): if true logs to the data file, in addiction to the system logger
+
+ Returns:
+ None
+ """
+ get_context().scriptingLog(str(log))
+ if data_file:
+ try:
+ get_context().dataManager.appendLog(str(log))
+ except:
+ #Do not generate exception if cannot write to data file
+ pass
+
+def set_exec_pars(**args):
+ """ Configures the script execution parameters, overriding the system configuration.
+
+ Args:
+ args(dictionary). Keys:
+ name(str, optional): value of the {name} tag. Default is the running script name
+ (or "scan" in the case of a command line scan command.)
+ type(str, optional): value of the {type} tag. Default is empty.
+ This field can be used to store data in sub-folders of standard location.
+ path(str, optional): If defined provides the full path name for data output root (overriding config))
+ The tag {data} can be used to enter a path relative to the standard data folder.
+ layout(str, optional): Overrides default data layout.
+ provider(str, optional): Overrides default data provider.
+ depth_dim(int, optional): dimension of 2d-matrixes in 3d datasets.
+ save(bool, optional): Overrides the configuration option to auto save scan data.
+ flush(bool, optional): Overrides the configuration option to flush file on each record.
+ keep(bool, optional): Overrides the configuration option to release scan records.
+ If false disable accumulation of scan records to scan result.
+ preserve(bool, optional): Overrides the configuration option to preserve device types.
+ If false all values are converted to double.
+ open(bool, optional): If true opens data output root (instead of only doing in the first data access call)
+ If false closes output root, if open.
+ reset(bool, optional): If true reset the scan counter - the {count} tag and set the timestamp to now.
+ group(str, optional): Overrides default layout group name for scans
+ tag(str, optional): Overrides default tag for scan names (affecting group or dataset name, according to layout)
+ defaults(bool, optional): If true restore the original execution parameters.
+
+ Graphical preferences can also be set. Keys are equal to lowercase of Preference enum:
+ "plot_disabled", "table_disabled", "enabled_plots", "plot_types", "print_scan", "auto_range",
+ "manual_range","domain_axis", "status".
+ See set_preference for more information.
+
+ Shortcut entries: "line_plots": list of devices with enforced line plots.
+ "range": "none", "auto", or [min, max]
+ "display": if false disables scan data plotting and printing.
+ """
+ get_context().setExecutionPars(args)
+
+def get_exec_pars():
+ """ Returns script execution parameters.
+
+ Returns:
+ ExecutionParameters object. Fields:
+ name (str): execution name - {name} tag.
+ type (str): execution type - {type} tag.
+ path (str): output data root.
+ open (bool): true if the output data root has been opened.
+ layout (str): data output layout. If None then using the configuration.
+ save (bool): auto save scan data option.
+ flush (bool): flush file on each record.
+ index (int): current scan index.
+ group (str): data group currently used for scan data storage.
+ if no ongoing scan return "/" if within a script, or else None if a console command.
+ scanPath (str): dataset or group corresponding to current scan.
+ scan (Scan): reference to current scan, if any
+ source (CommandSource): return the source of the script or command.
+ background (bool): return False if executing in main interpreter thread .
+ aborted (bool): True if execution has been aborted
+ """
+ return get_context().getExecutionPars()
+
+
+###################################################################################################
+#EPICS channel access
+###################################################################################################
+
+def _adjust_channel_value(value, var_type=None):
+ if (value is None):
+ return value
+ if (var_type is not None):
+ if is_list(value):
+ var_type = var_type.replace(',','').replace('[','')
+ ret = []
+ for item in value:
+ ret.append(_adjust_channel_value(item), var_type)
+ value = ret
+ else:
+ var_type = var_type.lower()
+ if var_type=='b':
+ value = byte(value)
+ elif var_type=='i':
+ value = short(value)
+ elif var_type=='l':
+ value = int(value)
+ elif var_type=='f':
+ value = float(value)
+ elif var_type=='d':
+ value = float(value)
+ elif var_type=='s':
+ value = str(value)
+
+ if isinstance(value,tuple):
+ value = list(value)
+ if isinstance(value,list):
+ list_type = type(value[0])
+ array_types = {
+ int: "i",
+ long: "l",
+ float:"d",
+ str:Class.forName("java.lang.String"),
+ }
+ array_type = array_types.get(type(value[0]),'d')
+ array = PyArray(array_type)
+ array.fromlist(value)
+ value=array
+ return value
+
+def caget(name, type=None, size=None, meta = False ):
+ """Reads an Epics PV.
+
+ Args:
+ name(str): PV name
+ type(str, optional): type of PV. By default gets the PV standard field type.
+ Scalar values: 'b', 'i', 'l', 'd', 's'.
+ Array: values: '[b', '[i,', '[l', '[d', '[s'.
+ size (int, optional): for arrays, number of elements to be read. Default read all.
+ meta (bool, optional): if true gets channel value and metadata (timestamp, severity).
+
+ Returns:
+ PV value if meta is false, otherwise a dictionary containing PV value and metadata
+
+ """
+ if meta:
+ return Epics.getMeta(name, Epics.getChannelType(type), size)
+ return Epics.get(name, Epics.getChannelType(type), size)
+
+def cawait(name, value, timeout=None, comparator=None, type=None, size=None):
+ """Wait for a PV to have a given value.
+
+ Args:
+ name(str): PV name
+ value (obj): value to compare to
+ timeout(float, optional): time in seconds to wait. If None, waits forever.
+ comparator(java.util.Comparator or float, optional): if None waits for equality.
+ If a numeric value is provided, waits for channel to be in range.
+ type(str, optional): type of PV. By default gets the PV standard field type.
+ Scalar values: 'b', 'i', 'l', 'd', 's'.
+ Array: values: '[b', '[i,', '[l', '[d', '[s'.
+ size (int, optional): for arrays, number of elements to be read. Default read all.
+
+ Returns:
+ None
+ """
+ if (timeout is not None):
+ timeout = int(timeout*1000)
+ value = _adjust_channel_value(value)
+ Epics.waitValue(name, value, comparator, timeout, Epics.getChannelType(type), size)
+
+def caput(name, value, timeout = None):
+ """Writes to an Epics PV.
+
+ Args:
+ name(str): PV name
+ value(scalar, string or array): new PV value.
+ timeout(int, optional): timeout in seconds to the write. If None waits forever to completion.
+
+ Returns:
+ None
+ """
+ value=_adjust_channel_value(value)
+ if (timeout is not None):
+ timeout = int(timeout*1000)
+ return Epics.put(name, value, timeout)
+
+def caputq(name, value):
+ """Writes to an Epics PV and does not wait.
+
+ Args:
+ name(str): PV name
+ value(scalar, string or array): new PV value.
+
+ Returns:
+ None
+ """
+ value=_adjust_channel_value(value)
+ return Epics.putq(name, value)
+
+def camon(name, type=None, size=None, wait = sys.maxint):
+ """Install a monitor to an Epics PV and print value changes.
+
+ Args:
+ name(str): PV name
+ type(str, optional): type of PV. By default gets the PV standard field type.
+ Scalar values: 'b', 'i', 'l', 'd', 's'.
+ Array: values: '[b', '[i,', '[l', '[d', '[s'.
+ size (int, optional): for arrays, number of elements to be read. Default read all.
+ wait (int, optional): blocking time for this function. By default blocks forever.
+ Returns:
+ None
+
+ """
+ val = lambda x: x.tolist() if isinstance(x,PyArray) else x
+
+ class MonitorListener(java.beans.PropertyChangeListener):
+ def propertyChange(self, pce):
+ print val(pce.getNewValue())
+
+ channel = create_channel(name, type, size)
+ print val(channel.getValue())
+ channel.setMonitored(True)
+ channel.addPropertyChangeListener(MonitorListener())
+
+ try:
+ time.sleep(wait)
+ finally:
+ Epics.closeChannel(channel)
+
+def create_channel_device(channel_name, type=None, size=None, device_name=None):
+ """Create a device from an EPICS PV.
+
+ Args:
+ channel_name(str): PV name
+ type(str, optional): type of PV. By default gets the PV standard field type.
+ Scalar values: 'b', 'i', 'l', 'd', 's'.
+ Array: values: '[b', '[i,', '[l', '[d', '[s'.
+ size (int, optional): for arrays, number of elements to be read. Default read all.
+ device_name (str, optional): device name (if different from hannel_name.
+ Returns:
+ None
+
+ """
+ dev = Epics.newChannelDevice(channel_name if (device_name is None) else device_name , channel_name, Epics.getChannelType(type))
+ if get_context().isSimulation():
+ dev.setSimulated()
+ dev.initialize()
+ if (size is not None):
+ dev.setSize(size)
+ return dev
+
+def create_channel(name, type=None, size=None):
+ return Epics.newChannel(name, Epics.getChannelType(type), size)
+
+class Channel(java.beans.PropertyChangeListener, Writable, Readable):
+ def __init__(self, channel_name, type = None, size = None, callback=None, alias = None):
+ """ Create an object that encapsulates an Epics PV connection.
+ Args:
+ channel_name(str):name of the channel
+ type(str, optional): type of PV. By default gets the PV standard field type.
+ Scalar values: 'b', 'i', 'l', 'd', 's'.
+ Array: values: '[b', '[i,', '[l', '[d', '[s'.
+ size(int, optional): the size of the channel
+ callback(function, optional): The monitor callback.
+ alias(str): name to be used on scans.
+ """
+ self.channel = create_channel(channel_name, type, size)
+ self.callback = callback
+ if alias is not None:
+ set_device_alias(self, alias)
+ else:
+ set_device_alias(self, channel_name)
+
+ def get_name(self):
+ """Return the name of the channel.
+ """
+ return self.channel.name
+
+ def get_size(self):
+ """Return the size of the channel.
+ """
+ return self.channel.size
+
+ def set_size(self, size):
+ """Set the size of the channel.
+ """
+ self.channel.size = size
+
+ def is_connected(self):
+ """Return True if channel is connected.
+ """
+ return self.channel.connected
+
+ def is_monitored(self):
+ """Return True if channel is monitored
+ """
+ return self.channel.monitored
+
+ def set_monitored(self, value):
+ """Set a channel monitor to trigger the callback function defined in the constructor.
+ """
+ self.channel.monitored = value
+ if (value):
+ self.channel.addPropertyChangeListener(self)
+ else:
+ self.channel.removePropertyChangeListener(self)
+
+ def propertyChange(self, pce):
+ if pce.getPropertyName() == "value":
+ if self.callback is not None:
+ self.callback(pce.getNewValue())
+
+ def put(self, value, timeout=None):
+ """Write to channel and wait value change. In the case of a timeout throws a TimeoutException.
+ Args:
+ value(obj): value to be written
+ timeout(float, optional): timeout in seconds. If none waits forever.
+ """
+ if (timeout==None):
+ self.channel.setValue(value)
+ else:
+ self.channel.setValueAsync(value).get(int(timeout*1000), java.util.concurrent.TimeUnit.MILLISECONDS);
+
+ def putq(self, value):
+ """Write to channel and don't wait.
+ """
+ self.channel.setValueNoWait(value)
+
+ def get(self, force = False):
+ """Get channel value.
+ """
+ return self.channel.getValue(force)
+
+ def wait_for_value(self, value, timeout=None, comparator=None):
+ """Wait channel to reach a value, using a given comparator. In the case of a timeout throws a TimeoutException.
+ Args:
+ value(obj): value to be verified.
+ timeout(float, optional): timeout in seconds. If None waits forever.
+ comparator (java.util.Comparator, optional). If None, uses Object.equals.
+ """
+ if comparator is None:
+ if timeout is None:
+ self.channel.waitForValue(value)
+ else:
+ self.channel.waitForValue(value, int(timeout*1000))
+ else:
+ if timeout is None:
+ self.channel.waitForValue(value, comparator)
+ else:
+ self.channel.waitForValue(value, comparator, int(timeout*1000))
+
+ def close(self):
+ """Close the channel.
+ """
+ self.channel.destroy()
+
+ #Writable interface
+ def write(self, value):
+ self.put(value)
+
+ #Readable interface
+ def read(self):
+ return self.get()
+
+
+###################################################################################################
+#Concurrent execution
+###################################################################################################
+
+class Callable(java.util.concurrent.Callable):
+ def __init__(self, method, *args):
+ self.method = method
+ self.args = args
+ self.thread = java.lang.Thread.currentThread()
+ def call(self):
+ try:
+ get_context().startedChildThread(self.thread)
+ return self.method(*self.args)
+ #except:
+ # traceback.print_exc(file=sys.stderr)
+ finally:
+ get_context().finishedChildThread(self.thread)
+
+def fork(*functions):
+ """Start execution of functions in parallel.
+
+ Args:
+ *functions(function references)
+
+ Returns:
+ List of callable objects
+ """
+ callables = []
+ for m in functions:
+ if is_list(m):
+ callables.append(Callable(m[0],*m[1]))
+ else:
+ callables.append(Callable(m))
+ return Threading.fork(callables)
+
+def join(futures):
+ """Wait parallel execution of functions.
+
+ Args:
+ futures(list of Future) : as returned from fork
+
+ Returns:
+ None
+"""
+ try:
+ return Threading.join(futures)
+ except java.util.concurrent.ExecutionException, ex:
+ raise ex.getCause()
+
+def parallelize(*functions):
+ """Equivalent to fork + join
+
+ Args:
+ *functions(function references)
+
+ Returns:
+ None
+ """
+ futures = fork(*functions)
+ return join(futures)
+
+
+###################################################################################################
+#Script evaluation and background task control.
+###################################################################################################
+
+def run(script_name, args = None, locals = None):
+ """Run script: can be absolute path, relative, or short name to be search in the path.
+ Args:
+ args(Dict ot List): Sets Sys.argv (if list) or gobal variables(if dict) to the script.
+ locals(Dict): If not none sets the locals()for the runing script.
+ If locals is used then script definitions will not go to global namespace.
+
+ Returns:
+ The script return value (if set with set_return)
+ """
+ global _
+ script = get_context().scriptManager.library.resolveFile(script_name)
+ if script is not None and os.path.isfile(script):
+ get_context().startScriptExecution(args)
+ _ = None
+ if args is None:
+ pass
+ elif isinstance(args,tuple):
+ sys.argv = list(args)
+ elif isinstance(args,list):
+ sys.argv = args
+ else:
+ for arg in args.keys():
+ globals()[arg] = args[arg]
+ if (locals is None):
+ execfile(script, globals())
+ else:
+ execfile(script, globals(), locals)
+ return _
+ raise IOError("Invalid script: " + str(script_name))
+
+def abort():
+ """Abort the execution of ongoing task. It can be called from the script to quit.
+
+ Args:
+ None
+
+ Returns:
+ None
+ """
+ #Cannot be on script execution thread
+ fork(get_context().abort)
+
+def start_task(script, delay = 0.0, interval = -1):
+ """Start a background task
+
+ Args:
+ script(str): Name of the script implementing the task
+ delay(float, optional): time in seconds for the first execution.
+ Default starts immediately.
+ interval(float, optional): time in seconds for between execution.
+ If negative (default), single-execution.
+
+ Returns:
+ None
+ """
+ delay_ms=int(delay*1000)
+ interval_ms=int(interval*1000) if (interval>=0) else int(interval)
+ get_context().taskManager.create(script, delay_ms, interval_ms)
+ get_context().taskManager.start(script)
+
+def stop_task(script, force = False):
+ """Stop a background task
+
+ Args:
+ script(str): Name of the script implementing the task
+ force(boolean, optional): interrupt current execution, if running
+
+ Returns:
+ None
+ """
+ get_context().taskManager.remove(script, force)
+
+def set_return(value):
+ """Sets the script return value. This value is returned by the "run" function.
+
+ Args:
+ value(Object): script return value.
+
+ Returns:
+ None
+ """
+ #In Jython, the output of last statement is not returned when running a file
+ if __name__ == "__builtin__":
+ global __THREAD_EXEC_RESULT__
+ if is_interpreter_thread():
+ global _
+ _=value
+ __THREAD_EXEC_RESULT__[java.lang.Thread.currentThread()]=value #Used when running file
+ else:
+ #if startup is imported, cannot set global
+ caller = _get_caller()
+ if is_interpreter_thread():
+ caller.f_globals["_"]=value
+ if not "__THREAD_EXEC_RESULT__" in caller.f_globals.keys():
+ caller.f_globals["__THREAD_EXEC_RESULT__"] = {}
+ caller.f_globals["__THREAD_EXEC_RESULT__"][java.lang.Thread.currentThread()]=value
+ return value #Used when parsing file
+
+def get_return():
+ if __name__ == "__builtin__":
+ global __THREAD_EXEC_RESULT__
+ return __THREAD_EXEC_RESULT__[java.lang.Thread.currentThread()]
+ else:
+ return _get_caller().f_globals["__THREAD_EXEC_RESULT__"][java.lang.Thread.currentThread()]
+
+def is_interpreter_thread():
+ return java.lang.Thread.currentThread().name == "Interpreter Thread"
+
+
+###################################################################################################
+#Versioning tools
+###################################################################################################
+
+def commit(message, force = False):
+ """Commit the changes to the repository. If manual commit is not configured then there is no need to call this function: commits are made as needed.
+
+ Args:
+ message(str): commit message
+ force(bool, optional): if False, raises exception if no change detected in repo
+
+ Returns:
+ None
+ """
+ get_context().commit(message, force)
+
+def diff():
+ """Return list of changes in the repository
+
+ Args:
+ None
+
+ Returns:
+ None
+ """
+ return get_context().diff()
+
+def checkout_tag(tag):
+ """Checkout a tag name.
+
+ Args:
+ tag(str): tag name.
+
+ Returns:
+ None
+ """
+ get_context().checkoutTag(tag)
+
+def checkout_branch(tag):
+ """Checkout a local branch name.
+
+ Args:
+ tag(str): branch name.
+
+ Returns:
+ None
+ """
+ get_context().checkoutLocalBranch(tag)
+
+def pull_repository():
+ """Pull from remote repository.
+
+ """
+ get_context().pullFromUpstream()
+
+def push_repository(all_branches=True, force=False):
+ """Push to remote repository.
+
+ Args:
+ all_branches(boolean, optional): all branches or just current.
+ force(boolean, optional): force flag.
+
+ Returns:
+ None
+ """
+ get_context().pushToUpstream(all_branches, force)
+
+def cleanup_repository():
+ """Performs a repository cleanup.
+
+ Args:
+ None
+
+ Returns:
+ None
+ """
+ get_context().cleanupRepository()
+
+
+###################################################################################################
+#Device Pool functions
+###################################################################################################
+
+def get_device(device_name):
+ """Returns a configured device (or imaging source) by its name.
+
+ Args:
+ device_name(str): name of the device.
+
+ Returns:
+ device
+ """
+ return get_context().devicePool.getByName(device_name)
+
+def add_device(device, force = False):
+ """Add a device (or imaging source) to the device pool.
+
+ Args:
+ device(Device or Source): device object.
+ force(boolean, optional): if true then dispose existing device with same name.
+ Otherwise will fail in case of name clash.
+
+ Returns:
+ True if device was added, false if was already in the pool, or exception in case of name clash.
+ """
+ if get_context().devicePool.contains(device):
+ return False
+ if force:
+ dev = get_context().devicePool.getByName(device.getName())
+ if dev is not None:
+ remove_device(dev)
+ return get_context().devicePool.addDevice(device)
+
+def remove_device(device):
+ """Remove a device (or imaging source) from the device pool.
+
+ Args:
+ device(Device or Source): device object.
+
+ Returns:
+ bool: true if device was removed.
+
+ """
+ return get_context().devicePool.removeDevice(device)
+
+def set_device_alias(device, alias):
+ """Set a device alias to be used in scans (datasets and plots).
+
+ Args:
+ device(Device): device object.
+ alias(str): replace device name in scans.
+
+ Returns:
+ None
+ """
+ get_context().dataManager.setAlias(device, alias)
+
+def stop():
+ """Stop all devices implementing the Stoppable interface.
+
+ Args:
+ None
+
+ Returns:
+ None
+ """
+ get_context().stopAll()
+
+def update():
+ """Update all devices.
+
+ Args:
+ None
+
+ Returns:
+ None
+ """
+ get_context().updateAll()
+
+def reinit(dev = None):
+ """Re-initialize devices.
+
+ Args:
+ dev(Device, optional): the device to be re-initialized.
+ If None re-initialize all devices not yet initialized.
+
+ Returns:
+ List with devices not initialized.
+ """
+ return to_list(get_context().reinit())
+
+def create_device(url, parent=None):
+ """Create a device form a definition string(see InlineDevice)
+
+ Args:
+ url(str or list of string): the device definition string (or list of strings)
+ parent(bool, optional): parent device
+
+ Returns:
+ The created device (or list of devices)
+ """
+ return InlineDevice.create(url, parent)
+
+
+def create_averager(dev, count, interval=0.0, name = None, monitored = False):
+ """Creates and initializes and averager for dev.
+
+ Args:
+ dev(Device): the source device
+ count(int): number of samples
+ interval(float, optional): sampling interval in seconds.
+ If less than zero, sampling is made on data change event.
+ name(str, optional): sets the name of the device (default is: averager)
+ monitored (bool, optional): if true then averager processes asynchronously.
+
+ Returns:
+ Averager device
+ """
+ dev = string_to_obj(dev)
+ if isinstance(dev, ReadableArray):
+ av = ArrayAverager(dev, count, int(interval*1000)) if (name is None) else ArrayAverager(name, dev, count, int(interval*1000))
+ else:
+ av = Averager(dev, count, int(interval*1000)) if (name is None) else Averager(name, dev, count, int(interval*1000))
+ av.initialize()
+ if (monitored):
+ av.monitored = True
+ return av
+
+
+###################################################################################################
+#Standard libraries management
+###################################################################################################
+
+if __name__ == "__builtin__":
+ ca_channel_path=os.path.join(get_context().setup.getStandardLibraryPath(), "epics")
+ sys.path.append(ca_channel_path)
+ #This is to destroy previous context of _ca (it is not shared with PShell)
+ if run_count > 0:
+ if sys.modules.has_key("_ca"):
+ import _ca
+ _ca.initialize()
+
+
+###################################################################################################
+#Mathematical functions
+###################################################################################################
+
+def arrmul(a, b):
+ """Multiply 2 series of the same size.
+
+ Args:
+
+ a(list, tuple, array ...): subscriptable object containing numbers
+ b(list, tuple, array ...): subscriptable object containing numbers
+
+ Returns:
+ List
+
+ """
+ return map(mul, a, b)
+
+def arrdiv(a, b):
+ """Divide 2 series of the same size.
+
+ Args:
+
+ a(list, tuple, array ...): subscriptable object containing numbers
+ b(list, tuple, array ...): subscriptable object containing numbers
+
+ Returns:
+ List
+
+ """
+ return map(truediv, a, b)
+
+def arradd(a, b):
+ """Add 2 series of the same size.
+
+ Args:
+
+ a(list, tuple, array ...): subscriptable object containing numbers
+ b(list, tuple, array ...): subscriptable object containing numbers
+
+ Returns:
+ List
+
+ """
+ return map(add, a, b)
+
+def arrsub(a, b):
+ """Subtract 2 series of the same size.
+
+ Args:
+
+ a(list, tuple, array ...): subscriptable object containing numbers
+ b(list, tuple, array ...): subscriptable object containing numbers
+
+ Returns:
+ List
+
+ """
+ return map(sub, a, b)
+
+def arrabs(a):
+ """Returns the absolute of all elements in series.
+
+ Args:
+
+ a(list, tuple, array ...): subscriptable object containing numbers
+
+ Returns:
+ List
+
+ """
+ return map(abs, a)
+
+def arroff(a, value = "mean"):
+ """Subtract offset to all elemets in series.
+
+ Args:
+
+ a(list, tuple, array ...): subscriptable object containing numbers
+ type(int or str, optional): value to subtract from the array, or "mean" or "min".
+
+ Returns:
+ List
+
+ """
+ if value=="mean":
+ value = mean(a)
+ elif value=="min":
+ value = min(a)
+ return [x-value for x in a]
+
+def mean(data):
+ """Calculate the mean of a sequence.
+
+ Args:
+ data(list, tuple, array ...): subscriptable object containing numbers
+
+ Returns:
+ Mean of the elements in the object.
+
+ """
+ return reduce(lambda x, y: x + y, data) / len(data)
+
+def variance(data):
+ """Calculate the variance of a sequence.
+
+ Args:
+ data(list, tuple, array ...): subscriptable object containing numbers
+
+ Returns:
+ Variance of the elements in the object.
+
+ """
+ c = mean(data)
+ ss = sum((x-c)**2 for x in data)
+ return ss/len(data)
+
+def stdev(data):
+ """Calculate the standard deviation of a sequence.
+
+ Args:
+ data(list, tuple, array ...): subscriptable object containing numbers
+
+ Returns:
+ Standard deviation of the elements in the object.
+
+ """
+ return variance(data)**0.5
+
+
+def center_of_mass(data, x = None):
+ """Calculate the center of mass of a series, and its rms.
+
+ Args:
+
+ data(list, tuple, array ...): subscriptable object containing numbers
+ x(list, tuple, array ..., optional): x coordinates
+
+ Returns:
+ Tuple (com, rms)
+
+ """
+ if x is None:
+ x = Arr.indexesDouble(len(data))
+ data_sum = sum(data)
+ if (data_sum==0):
+ return float('nan')
+ xmd = arrmul( x, data)
+ com = sum(xmd) / data_sum
+ xmd2 = arrmul( x, xmd)
+ com2 = sum(xmd2) / data_sum
+ rms = math.sqrt(abs(com2 - com * com))
+ return (com, rms)
+
+def poly(val, coefs):
+ """Evaluates a polinomial: (coefs[0] + coefs[1]*val + coefs[2]*val^2...
+
+ Args:
+ val(float): value
+ coefs (list of loats): polinomial coefficients
+ Returns:
+ Evaluated function for val
+
+ """
+ r = 0
+ p = 0
+ for c in coefs:
+ r = r + c * math.pow(val, p)
+ p = p + 1
+ return r
+
+def histogram(data, range_min = None, range_max = None, bin = 1.0):
+ """Creates histogram on data.
+
+ Args:
+ data (tuple, array, ArrayList or Array): input data can be multi-dimensional or nested.
+ range_min (int, optional): minimum histogram value. Default is floor(min(data))
+ range_max (int, optional): maximul histogram value. Default is ceil(max(data))
+ bin(int or float, optional): if int means number of bins. If float means bin size. Default = 1.0.
+ Returns:
+ tuple: (ydata, xdata)
+
+ """
+ if range_min is None: range_min = math.floor(min(flatten(data)))
+ if range_max is None: range_max = math.ceil(max(flatten(data)))
+ if type(bin) is float:
+ bin_size = bin
+ n_bin = int(math.ceil(float(range_max - range_min)/bin_size))
+ else:
+ n_bin = bin
+ bin_size = float(range_max - range_min)/bin
+
+ result = [0] * n_bin
+ for d in flatten(data):
+ b = int( float(d - range_min) / bin_size)
+ if (b >=0) and (b < n_bin):
+ result[b] = result[b] + 1
+ return (result, frange(range_min, range_max, bin_size))
+
+def _turn(p, q, r):
+ return cmp((q[0] - p[0])*(r[1] - p[1]) - (r[0] - p[0])*(q[1] - p[1]), 0)
+
+def _keep(hull, r):
+ while len(hull) > 1 and _turn(hull[-2], hull[-1], r) != 1:
+ hull.pop()
+ return (not len(hull) or hull[-1] != r) and hull.append(r) or hull
+
+def convex_hull(point_list=None, x=None, y=None):
+ """Returns the convex hull from a list of points. Either point_list or x,y is provided.
+ (Alhorithm taken from http://tomswitzer.net/2010/03/graham-scan/)
+ Args:
+ point_list (array of tuples, optional): arrays of the points
+ x (array of float, optional): array with x coords of points
+ y (array of float, optional): array with y coords of points
+ Returns:
+ Array of points or (x,y)
+
+ """
+ is_point_list = point_list is not None
+ if not point_list:
+ point_list=[]
+ for i in range(len(x)):
+ if((x[i] is not None) and (y[i] is not None)): point_list.append((x[i], y[i]))
+ point_list.sort()
+ lh,uh = reduce(_keep, point_list, []), reduce(_keep, reversed(point_list), [])
+ ret = lh.extend(uh[i] for i in xrange(1, len(uh) - 1)) or lh
+ if not is_point_list:
+ x, y = [], []
+ for i in range(len(ret)):
+ x.append(ret[i][0])
+ y.append(ret[i][1])
+ return (x,y)
+ return ret
+
+###################################################################################################
+#Utilities
+###################################################################################################
+
+def get_setting(name=None):
+ """Get a persisted script setting value.
+
+ Args:
+ name (str): name of the setting.
+ Returns:
+ String with setting value or None if setting is undefined.
+ If name is None then returns map with all settings.
+ """
+ return get_context().getSettings() if (name is None) else get_context().getSetting(name)
+
+def set_setting(name, value):
+ """Set a persisted script setting value.
+
+ Args:
+ name (str): name of the setting.
+ value (obj): value for the setting, converted to string (if None then remove the setting).
+ Returns:
+ None.
+ """
+ get_context().setSetting(name, value)
+
+def exec_cmd(cmd):
+ """Executes a shell command. If errors happens raises an exception.
+
+ Args:
+ cmd (str): command process input.
+ Returns:
+ Output of command process.
+ """
+ import subprocess
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+ (ret, err) = proc.communicate()
+ if (err is not None) and err!="":
+ raise Exception(err)
+ return ret
+
+def exec_cpython(script_name, args = [], method_name = None, python_name = "python"):
+ """Executes an external cpython process.
+
+ Args:
+ script_name (str): name of the script (can be absolute or relative to script folder).
+ args(list, optional): arguments to python process (or parameters to method, if not None)
+ method_name (str, optional): if defined indicates a method to be called.
+ python_name (str, optional): name of executable
+ Returns:
+ Return of python process.
+ """
+ if method_name is None:
+ script = get_context().scriptManager.library.resolveFile(script_name)
+ if script is None :
+ script= os.path.abspath(script_name)
+ c = python_name + " " + script + " "
+ if args is not None and (len(args)>0):
+ for arg in args:
+ c = c + str(arg) + " "
+ return exec_cmd(c)
+ else:
+ #Calling a method
+ import json
+ import tempfile
+ script = os.path.abspath(get_context().scriptManager.library.resolveFile(script_name))
+ with open(get_context().setup.getContextPath()+ "/Temp" + str(java.lang.Thread.currentThread().getId())+".py", "wb") as f:
+ f.write(("script = '" +script +"'\n").replace('\\', '\\\\'))
+ f.write("function = '" +method_name +"'\n")
+ f.write("jsonargs = '" + json.dumps(args) +"'\n")
+ f.write("""import sys
+import json
+import os
+args =json.loads(jsonargs)
+i = script.rfind(os.sep)
+module = script[i+1:-3]
+sys.path.insert(1,script[:i+1])
+exec ('from ' + module + ' import ' + function + ' as function')
+print (json.dumps(function(*args)))
+""")
+ f.close()
+ ret = exec_cpython(os.path.abspath(f.name), python_name = python_name)
+ os.remove(f.name)
+ ret = '\n'+ret[0:-len(os.linesep)]
+ jsonret = ret[ret.rfind('\n')+1:].strip()
+ return json.loads(jsonret)
+
+def bsget(channel, modulo=1, offset=0, timeout = 5.0):
+ """Reads an values a bsread stream, using the default provider.
+
+ Args:
+ channel(str or list of str): channel name(s)
+ module(int, optional): stream modulo
+ offset(int, optional): stream offset
+ timeout(float, optional): stream timeout in secs
+ Returns:
+ BS value or list of values
+
+ """
+ channels = to_list(channel)
+ ret = Stream.readChannels(channels, modulo, offset, int(timeout * 1000))
+ if is_string(channel):
+ return ret[0]
+ return ret
+
+def flatten(data):
+ """Flattens multi-dimentional or nested data.
+
+ Args:
+ data (tuple, array, ArrayList or Array): input data
+ Returns:
+ Iterator on the flattened data.
+
+ """
+ if isinstance(data,PyArray):
+ if not data.typecode.startswith('['):
+ return data
+
+ import itertools
+ return itertools.chain(*data)
+
+def frange_gen(start, finish, step):
+ while ((step >= 0.0) and (start <= finish)) or ((step < 0.0) and (start >= finish)):
+ yield start
+ start += step
+
+def frange(start, finish, step, enforce_finish = False, inclusive_finish = False):
+ """Create a list with a range of float values (a float equivalent to "range").
+
+ Args:
+ start(float): start of range.
+ finish(float): end of range.
+ step(float): step size.
+ enforce_finish(boolean, optional): adds the final element even if range was not exact.
+ inclusive_finish(boolean, optional): if false finish is exclusive (like in "range").
+
+ Returns:
+ list
+
+ """
+ step = float(step)
+ ret = list(frange_gen(start, finish, step))
+ if len(ret) > 0:
+ if inclusive_finish == False:
+ if ret[-1]==finish:
+ del ret[-1]
+ if enforce_finish and ret[-1]!=finish:
+ ret.append(finish)
+ return ret
+
+def _get_caller():
+ #Not doing inspect.currentframe().f_back because inspect is slow to load
+ return sys._getframe(1).f_back if hasattr(sys, "_getframe") else None
+
+def inject():
+ """Restore initial globals: re-inject devices and startup variables to the interpreter.
+
+ Args:
+ None
+
+ Returns:
+ None
+
+ """
+ if __name__ == "__builtin__":
+ get_context().injectVars()
+ else:
+ _get_caller().f_globals.update(get_context().scriptManager.injections)
+
+def notify(subject, text, attachments = None, to=None):
+ """Send email message.
+
+ Args:
+ subject(str): Message subject.
+ text(str): Message body.
+ attachments(list of str, optional): list of files to be attached (expansion tokens are allowed).
+ to (list ofd str, optional): recipients. If None uses the recipients defined in mail.properties.
+ Returns:
+ None
+
+ """
+ get_context().notify(subject, text, to_list(attachments), to_list(to))
+
+def string_to_obj(o):
+ if is_string(o):
+ if "://" in o:
+ return InlineDevice(o)
+ return eval(o)
+ elif is_list(o):
+ ret = []
+ for i in o:
+ ret.append(string_to_obj(i))
+ return ret
+ return o
+
+def _getBuiltinFunctions(filter = None):
+ ret = []
+ for name in globals().keys():
+ val = globals()[name]
+ if type(val) is PyFunction:
+ if filter is None or filter in name:
+ #Only "public" documented functions
+ if not name.startswith('_') and (val.__doc__ is not None):
+ ret.append(val)
+ return to_array(ret)
+
+
+def getBuiltinFunctionNames(filter = None):
+ ret = []
+ for function in _getBuiltinFunctions(filter):
+ ret.append(function.func_name)
+ return to_array(ret)
+
+def getFunctionDoc(function):
+ if is_string(function):
+ if function not in globals():
+ return
+ function = globals()[function]
+ if type(function) is PyFunction and '__doc__' in dir(function):
+ ac = function.func_code.co_argcount
+ var = function.func_code.co_varnames
+ args = list(var)[:ac]
+ defs = function.func_defaults
+ if defs is not None:
+ for i in range (len(defs)):
+ index = len(args) - len(defs) + i
+ args[index] = args[index] + " = " + str(defs[i])
+ flags = function.func_code.co_flags
+ if flags & 4 > 0:
+ args.append('*' + var[ac])
+ ac=ac+1
+ if flags & 8 > 0:
+ args.append('**' + var[ac])
+ d = function.func_doc
+ return function.func_name+ "(" + ", ".join(args) + ")" + "\n\n" + (d if (d is not None) else "")
+
+def help(object = None):
+ """
+ Print help message for function or object (if available).
+
+ Args:
+ object (any, optional): function or object to get help.
+ If null prints a list of the builtin functions.
+
+ Returns:
+ None
+
+ """
+ if object is None:
+ print "Built-in functions:"
+ for f in getBuiltinFunctionNames():
+ print "\t" + f
+ else:
+ if type(object) is PyFunction:
+ print getFunctionDoc(object)
+ elif '__doc__' in dir(object):
+ #The default doc is now shown
+ import org.python.core.BuiltinDocs.object_doc
+ if object.__doc__ != org.python.core.BuiltinDocs.object_doc:
+ print object.__doc__
+
+###################################################################################################
+#UI interaction
+###################################################################################################
+
+def set_status(status):
+ """Set the application status.
+
+ Args:
+ status(str): new status.
+
+ Returns:
+ None
+ """
+ set_preference(Preference.STATUS, status)
+
+def setup_plotting( enable_plots=None, enable_table=None,plot_list = None, line_plots = None, range = None, domain=None, defaults=None):
+ if defaults == True: set_preference(Preference.DEFAULTS, True)
+ if enable_plots is not None: set_preference(Preference.PLOT_DISABLED, not enable_plots)
+ if enable_table is not None: set_preference(Preference.TABLE_DISABLED, not enable_table)
+ if plot_list is not None: set_preference(Preference.ENABLED_PLOTS, None if plot_list == "all" else plot_list)
+ if line_plots is not None:
+ plots = None
+ if line_plots != "none":
+ plots = {}
+ for plot in line_plots:
+ plots[plot]=1
+ set_preference(Preference.PLOT_TYPES, plots)
+ if range is not None:
+ if range == "none":
+ set_preference(Preference.AUTO_RANGE, None)
+ elif range == "auto":
+ set_preference(Preference.AUTO_RANGE, True)
+ else:
+ set_preference(Preference.MANUAL_RANGE, range)
+ if domain is not None:
+ set_preference(Preference.DOMAIN_AXIS, domain)
+
+
+def set_preference(preference, value):
+ """Hints to graphical layer:
+
+ Args:
+ preference(Preference): Preference name
+ Preference.PLOT_DISABLED #enable/disable scan plot (True/False)
+ Preference.TABLE_DISABLED #enable/disable scan table (True/False)
+ Preference.ENABLED_PLOTS #select Readables to be plotted (list of Readable or
+ String (Readable names))
+ Preference.PLOT_TYPES #Dictionary or (Readable or String):(String or int) pairs
+ where the key is a plot name and the value is the desired plot type
+ Preference.PRINT_SCAN #Print scan records to console
+ Preference.AUTO_RANGE # Automatic range scan plots x-axis
+ Preference.MANUAL_RANGE # Manually set scan plots x-axis
+ Preference.DOMAIN_AXIS #Set the domain axis source: "Time", "Index", or a readable name.
+ Default(None): first positioner
+ Preference.STATUS # set application status
+
+ value(object): preference value
+
+ Returns:
+ None
+ """
+ value = to_array(value, 'o') #If list then convert to Object array
+ get_context().setPreference(preference, value)
+
+def get_string(msg, default = None, alternatives = None, password = False):
+ """
+ Reads a string from UI
+ Args:
+ msg(str): display message.
+ default(str, optional): value displayed when window is shown.
+ alternatives(list of str, optional): if provided presents a combo box instead of an editing field.
+ password(boolean, optional): if True hides entered characters.
+
+ Returns:
+ String entered of null if canceled
+ """
+ if password :
+ return get_context().getPassword(msg, None)
+ return get_context().getString(msg, str(default) if (default is not None) else None, alternatives)
+
+def get_option(msg, type = "YesNoCancel"):
+ """
+ Gets an option from UI
+ Args:
+ msg(str): display message.
+ type(str, optional): 'YesNo','YesNoCancel' or 'OkCancel'
+
+ Returns:
+ 'Yes', 'No', 'Cancel'
+
+ """
+ return get_context().getOption(msg, type)
+
+def show_message(msg, title=None, blocking = True):
+ """
+ Pops a blocking message to UI
+
+ Args:
+ msg(str): display message.
+ title(str, optional): dialog title
+ """
+ get_context().showMessage(msg, title, blocking)
+
+def show_panel(device, title=None):
+ """
+ Show, if exists, the panel relative to this device.
+
+ Args:
+ device(Device or str or BufferedImage): device
+ title only apply to BufferedImage objects. For devices the title is the device name.
+ """
+ if type(device) is BufferedImage:
+ device = DirectSource(title, device)
+ device.initialize()
+ if is_string(device):
+ device = get_device(device)
+ return get_context().showPanel(device)
diff --git a/script/__Lib/diffcalc-2.1/.cache/v/cache/lastfailed b/script/__Lib/diffcalc-2.1/.cache/v/cache/lastfailed
new file mode 100755
index 0000000..9e26dfe
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/.cache/v/cache/lastfailed
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/.gitignore b/script/__Lib/diffcalc-2.1/.gitignore
new file mode 100755
index 0000000..edda307
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/.gitignore
@@ -0,0 +1,9 @@
+*.class
+*~
+*.pyc
+doc/build/
+.pydevproject
+.project
+.tox
+.DS_Store
+.idea
diff --git a/script/__Lib/diffcalc-2.1/.settings/com.wdev91.eclipse.copyright.xml b/script/__Lib/diffcalc-2.1/.settings/com.wdev91.eclipse.copyright.xml
new file mode 100755
index 0000000..eb2e1ed
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/.settings/com.wdev91.eclipse.copyright.xml
@@ -0,0 +1,29 @@
+
+
+ Diamond Light Source Ltd.
+ .]]>
+
+
+
diff --git a/script/__Lib/diffcalc-2.1/.settings/org.eclipse.core.resources.prefs b/script/__Lib/diffcalc-2.1/.settings/org.eclipse.core.resources.prefs
new file mode 100755
index 0000000..d0a9e4e
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+encoding//doc/source/conf.py=utf-8
+encoding/=UTF-8
diff --git a/script/__Lib/diffcalc-2.1/.settings/org.eclipse.core.runtime.prefs b/script/__Lib/diffcalc-2.1/.settings/org.eclipse.core.runtime.prefs
new file mode 100755
index 0000000..5a0ad22
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/script/__Lib/diffcalc-2.1/.travis.yml b/script/__Lib/diffcalc-2.1/.travis.yml
new file mode 100755
index 0000000..2ee17ea
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/.travis.yml
@@ -0,0 +1,21 @@
+# based on https://www.topbug.net/blog/2012/05/27/use-travis-ci-with-jython/
+
+language: python
+
+python:
+ - "2.7"
+
+env:
+ - JYTHON=false
+ - JYTHON=true
+
+install:
+ - if [ "$JYTHON" == "true" ]; then . install-jython-environment.sh; fi
+ - if [ "$JYTHON" == "false" ]; then pip install --upgrade pytest; pip install pytest-xdist; fi
+
+before_script:
+ - if [ "$JYTHON" == "true" ]; then export PYTEST=$HOME/jython/bin/pytest; else export PYTEST=pytest; fi
+ - echo PYTEST:- $PYTEST
+
+script: $PYTEST
+
diff --git a/script/__Lib/diffcalc-2.1/COPYING b/script/__Lib/diffcalc-2.1/COPYING
new file mode 100755
index 0000000..20d40b6
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ 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 .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/Makefile b/script/__Lib/diffcalc-2.1/Makefile
new file mode 100755
index 0000000..221a775
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/Makefile
@@ -0,0 +1,52 @@
+
+test-all: test-python test-jython test-integration test-launcher
+
+test-python:
+ py.test
+
+test-jython:
+ export CLASSPATH=$(HOME)/lib/Jama-1.0.3.jar:$(CLASSPATH); echo $$CLASSPATH; $(HOME)/jython/bin/py.test
+
+test-integration:
+ py.test --boxed integration_checks.py
+
+test-launcher:
+ ./diffcalc.py --help
+ ./diffcalc.py --modules
+ ./diffcalc.py --non-interactive --python sixcircle
+
+install-jython:
+ ./install-jython-environment.sh
+
+doc-source:
+ ./diffcalc.py --non-interactive --make-manuals-source
+
+doc-html:
+ cd doc; make html
+
+doc-pdf:
+ cd doc; make pdf
+
+doc-all:
+ cd doc; make all
+
+doc-clean:
+ cd doc; make clean
+
+help:
+ @echo
+ @echo "Please use \`make ' where is one of"
+ @echo
+ @echo " test-all"
+ @echo " test-python"
+ @echo " test-jython"
+ @echo " test-integration"
+ @echo " test-launcher"
+ @echo " install-jython"
+ @echo
+ @echo " doc-source : to expand *_template.rst to *.rst"
+ @echo " doc-html"
+ @echo " doc-pdf"
+ @echo " doc-all"
+ @echo " doc-clean"
+ @echo
diff --git a/script/__Lib/diffcalc-2.1/README.rst b/script/__Lib/diffcalc-2.1/README.rst
new file mode 100755
index 0000000..95f0ad3
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/README.rst
@@ -0,0 +1,513 @@
+Diffcalc - A Diffraction Condition Calculator for Diffractometer Control
+========================================================================
+
+Diffcalc is a python/jython based diffraction condition calculator used for
+controlling diffractometers within reciprocal lattice space. It performs the
+same task as the fourc, sixc, twoc, kappa, psic and surf macros from SPEC.
+
+There is a `user guide `_ and `developer guide `_, both at `diffcalc.readthedocs.io `_
+
+|Travis| |Read the docs|
+
+.. |Travis| image:: https://travis-ci.org/DiamondLightSource/diffcalc.svg?branch=master
+ :target: https://travis-ci.org/DiamondLightSource/diffcalc
+ :alt: Build Status
+
+.. |Read the docs| image:: https://readthedocs.org/projects/diffcalc/badge/?version=latest
+ :target: http://diffcalc.readthedocs.io/en/latest/?badge=latest
+ :alt: Documentation Status
+
+.. contents::
+
+.. section-numbering::
+
+Software compatibility
+----------------------
+
+- Written in Python using numpy
+- Works in Jython using Jama
+- Runs directly in `OpenGDA`
+- Runs in in Python or IPython using minimal OpenGda emulation (included)
+- Contact us for help running in your environment
+
+Diffractometer compatibility
+----------------------------
+
+Diffcalc’s standard calculation engine is an implementation of [You1999]_ and
+[Busing1967]_. Diffcalc works with any diffractometer which is a subset of:
+
+ .. image:: https://raw.githubusercontent.com/DiamondLightSource/diffcalc/master/doc/source/youmanual_images/4s_2d_diffractometer.png
+ :alt: 4s + 2d six-circle diffractometer, from H.You (1999)
+ :width: 50%
+ :align: center
+
+Diffcalc can be configured to work with any diffractometer geometry which is a
+subset of this. For example, a five-circle diffractometer might be missing the
+nu circle above.
+
+Note that the first versions of Diffcalc were based on [Vlieg1993]_ and
+[Vlieg1998]_ and a ‘Vlieg’ engine is still available. There is also an engine
+based on [Willmott2011]_. The ‘You’ engine is more generic and the plan is to
+remove the old ‘Vlieg’ engine once beamlines have been migrated.
+
+Installation
+------------
+
+Check it out::
+
+ $ git clone https://github.com/DiamondLightSource/diffcalc.git
+ Cloning into 'diffcalc'...
+
+At Diamond Diffcalc may be installed within an OpenGDA deployment and is
+available via the 'module' system from bash.
+
+Starting
+--------
+
+Start diffcalc in ipython using a sixcircle dummy diffractometer::
+
+ $ cd diffcalc
+ $ ./diffcalc.py --help
+ ...
+
+ $ ./diffcalc.py sixcircle
+
+ Running: "ipython --no-banner --HistoryManager.hist_file=/tmp/ipython_hist_zrb13439.sqlite -i -m diffcmd.start sixcircle False"
+
+ ---------------------------------- DIFFCALC -----------------------------------
+ Startup script: '/Users/zrb13439/git/diffcalc/startup/sixcircle.py'
+ Loading ub calculation: 'test'
+ ------------------------------------ Help -------------------------------------
+ Quick: https://github.com/DiamondLightSource/diffcalc/blob/master/README.rst
+ Manual: https://diffcalc.readthedocs.io
+ Type: > help ub
+ > help hkl
+ -------------------------------------------------------------------------------
+ In [1]:
+
+Within Diamond use::
+
+ $ module load diffcalc
+ $ diffcalc --help
+ ...
+ $ diffcalc sixcircle
+
+Trying it out
+-------------
+
+Type ``demo.all()`` to see it working and then move try the following quick
+start guide::
+
+ >>> demo.all()
+ ...
+
+Getting help
+------------
+
+To view help with orientation and then moving in hkl space::
+
+ >>> help ub
+ ...
+ >>> help hkl
+ ...
+
+Configuring a UB calculation
+----------------------------
+See the full `user manual for many more
+options and an explanation of what this all means.
+
+To load the last used UB-calculation::
+
+ >>> lastub
+ Loading ub calculation: 'mono-Si'
+
+To load a previous UB-calculation::
+
+ >>> listub
+ UB calculations in: /Users/walton/.diffcalc/i16
+
+ 0) mono-Si 15 Feb 2017 (22:32)
+ 1) i16-32 13 Feb 2017 (18:32)
+
+ >>> loadub 0
+
+To create a new UB-calculation::
+
+ >>> newub 'example'
+ >>> setlat '1Acube' 1 1 1 90 90 90
+
+Find U matrix from two reflections::
+
+ >>> pos wl 1
+ wl: 1.0000
+ >>> c2th [0 0 1]
+ 59.99999999999999
+
+ >>> pos sixc [0 60 0 30 90 0]
+ sixc: mu: 0.0000 delta: 60.0000 gam: 0.0000 eta: 30.0000 chi: 90.0000 phi: 0.0000
+ >>> addref [0 0 1]
+
+ >>> pos sixc [0 90 0 45 45 90]
+ sixc: mu: 0.0000 delta: 90.0000 gam: 0.0000 eta: 45.0000 chi: 45.0000 phi: 90.0000
+ >>> addref [0 1 1]
+ Calculating UB matrix.
+
+
+Check that it looks good::
+
+ >>> checkub
+
+ ENERGY H K L H_COMP K_COMP L_COMP TAG
+ 1 12.3984 0.00 0.00 1.00 0.0000 0.0000 1.0000
+ 2 12.3984 0.00 1.00 1.00 0.0000 1.0000 1.0000
+
+To see the resulting UB-calculation::
+
+ >>> ub
+ UBCALC
+
+ name: example
+
+ n_phi: 0.00000 0.00000 1.00000 <- set
+ n_hkl: -0.00000 0.00000 1.00000
+ miscut: None
+
+ CRYSTAL
+
+ name: 1Acube
+
+ a, b, c: 1.00000 1.00000 1.00000
+ 90.00000 90.00000 90.00000
+
+ B matrix: 6.28319 0.00000 0.00000
+ 0.00000 6.28319 0.00000
+ 0.00000 0.00000 6.28319
+
+ UB MATRIX
+
+ U matrix: 1.00000 0.00000 0.00000
+ 0.00000 1.00000 0.00000
+ 0.00000 0.00000 1.00000
+
+ U angle: 0
+
+ UB matrix: 6.28319 0.00000 0.00000
+ 0.00000 6.28319 0.00000
+ 0.00000 0.00000 6.28319
+
+ REFLECTIONS
+
+ ENERGY H K L MU DELTA GAM ETA CHI PHI TAG
+ 1 12.398 0.00 0.00 1.00 0.0000 60.0000 0.0000 30.0000 90.0000 0.0000
+ 2 12.398 0.00 1.00 1.00 0.0000 90.0000 0.0000 45.0000 45.0000 90.0000
+
+Setting the reference vector
+----------------------------
+See the full `user manual for many more
+options and an explanation of what this all means.
+
+By default the reference vector is set parallel to the phi axis. That is,
+along the z-axis of the phi coordinate frame.
+
+The `ub` command shows the current reference vector, along with any inferred
+miscut, at the top its report (or it can be shown by calling ``setnphi`` or
+``setnhkl'`` with no args)::
+
+ >>> ub
+ ...
+ n_phi: 0.00000 0.00000 1.00000 <- set
+ n_hkl: -0.00000 0.00000 1.00000
+ miscut: None
+ ...
+
+Constraining solutions for moving in hkl space
+----------------------------------------------
+See the full `user manual for many more
+options and an explanation of what this all means.
+
+To get help and see current constraints::
+
+ >>> help con
+ ...
+
+ >>> con
+ DET REF SAMP
+ ------ ------ ------
+ delta --> a_eq_b --> mu
+ --> gam alpha eta
+ qaz beta chi
+ naz psi phi
+ mu_is_gam
+
+ gam : 0.0000
+ a_eq_b
+ mu : 0.0000
+
+ Type 'help con' for instructions
+
+Three constraints can be given: zero or one from the DET and REF columns and the
+remainder from the SAMP column. Not all combinations are currently available.
+Use ``help con`` to see a summary if you run into troubles.
+
+To configure four-circle vertical scattering::
+
+ >>> con gam 0 mu 0 a_eq_b
+ gam : 0.0000
+ a_eq_b
+ mu : 0.0000
+
+Moving in hkl space
+-------------------
+
+Simulate moving to a reflection::
+
+ >>> sim hkl [0 1 1]
+ sixc would move to:
+ mu : 0.0000
+ delta : 90.0000
+ gam : 0.0000
+ eta : 45.0000
+ chi : 45.0000
+ phi : 90.0000
+
+ alpha : 30.0000
+ beta : 30.0000
+ naz : 35.2644
+ psi : 90.0000
+ qaz : 90.0000
+ tau : 45.0000
+ theta : 45.0000
+
+Move to reflection::
+
+ >>> pos hkl [0 1 1]
+ hkl: h: 0.00000 k: 1.00000 l: 1.00000
+
+ >>> pos sixc
+ sixc: mu: 0.0000 delta: 90.0000 gam: 0.0000 eta: 45.0000 chi: 45.0000 phi: 90.0000
+
+
+Scanning in hkl space
+---------------------
+
+Scan an hkl axis (and read back settings)::
+
+ >>> scan l 0 1 .2 sixc
+ l mu delta gam eta chi phi
+ ------- ------- -------- ------- -------- ------- --------
+ 0.00000 0.0000 60.0000 0.0000 30.0000 0.0000 90.0000
+ 0.20000 0.0000 61.3146 0.0000 30.6573 11.3099 90.0000
+ 0.40000 0.0000 65.1654 0.0000 32.5827 21.8014 90.0000
+ 0.60000 0.0000 71.3371 0.0000 35.6685 30.9638 90.0000
+ 0.80000 0.0000 79.6302 0.0000 39.8151 38.6598 90.0000
+ 1.00000 0.0000 90.0000 0.0000 45.0000 45.0000 90.0000
+
+Scan a constraint (and read back virtual angles and eta)::
+
+ >>> con psi
+ gam : 0.0000
+ ! psi : ---
+ mu : 0.0000
+ >>> scan psi 70 110 10 hklverbose [0 1 1] eta
+ psi eta h k l theta qaz alpha naz tau psi beta
+ -------- -------- ------- ------- ------- -------- -------- -------- -------- -------- -------- --------
+ 70.00000 26.1183 0.00000 1.00000 1.00000 45.00000 90.00000 19.20748 45.28089 45.00000 70.00000 42.14507
+ 80.00000 35.1489 -0.00000 1.00000 1.00000 45.00000 90.00000 24.40450 40.12074 45.00000 80.00000 35.93196
+ 90.00000 45.0000 0.00000 1.00000 1.00000 45.00000 90.00000 30.00000 35.26439 45.00000 90.00000 30.00000
+ 100.00000 54.8511 -0.00000 1.00000 1.00000 45.00000 90.00000 35.93196 30.68206 45.00000 100.00000 24.40450
+ 110.00000 63.8817 -0.00000 1.00000 1.00000 45.00000 90.00000 42.14507 26.34100 45.00000 110.00000 19.20748
+
+
+Orientation Commands
+--------------------
+
++-----------------------------+---------------------------------------------------+
+| **STATE** |
++-----------------------------+---------------------------------------------------+
+| **-- newub** {'name'} | start a new ub calculation name |
++-----------------------------+---------------------------------------------------+
+| **-- loadub** 'name' | num | load an existing ub calculation |
++-----------------------------+---------------------------------------------------+
+| **-- lastub** | load the last used ub calculation |
++-----------------------------+---------------------------------------------------+
+| **-- listub** | list the ub calculations available to load |
++-----------------------------+---------------------------------------------------+
+| **-- rmub** 'name'|num | remove existing ub calculation |
++-----------------------------+---------------------------------------------------+
+| **-- saveubas** 'name' | save the ub calculation with a new name |
++-----------------------------+---------------------------------------------------+
+| **LATTICE** |
++-----------------------------+---------------------------------------------------+
+| **-- setlat** | interactively enter lattice parameters (Angstroms |
+| | and Deg) |
++-----------------------------+---------------------------------------------------+
+| **-- setlat** name a | assumes cubic |
++-----------------------------+---------------------------------------------------+
+| **-- setlat** name a b | assumes tetragonal |
++-----------------------------+---------------------------------------------------+
+| **-- setlat** name a b c | assumes ortho |
++-----------------------------+---------------------------------------------------+
+| **-- setlat** name a b c | assumes mon/hex with gam not equal to 90 |
+| gamma | |
++-----------------------------+---------------------------------------------------+
+| **-- setlat** name a b c | arbitrary |
+| alpha beta gamma | |
++-----------------------------+---------------------------------------------------+
+| **-- c2th** [h k l] | calculate two-theta angle for reflection |
++-----------------------------+---------------------------------------------------+
+| **-- hklangle** [h1 k1 l1] | calculate angle between [h1 k1 l1] and [h2 k2 l2] |
+| [h2 k2 l2] | crystal planes |
++-----------------------------+---------------------------------------------------+
+| **REFERENCE (SURFACE)** |
++-----------------------------+---------------------------------------------------+
+| **-- setnphi** {[x y z]} | sets or displays n_phi reference |
++-----------------------------+---------------------------------------------------+
+| **-- setnhkl** {[h k l]} | sets or displays n_hkl reference |
++-----------------------------+---------------------------------------------------+
+| **REFLECTIONS** |
++-----------------------------+---------------------------------------------------+
+| **-- showref** | shows full reflection list |
++-----------------------------+---------------------------------------------------+
+| **-- addref** | add reflection interactively |
++-----------------------------+---------------------------------------------------+
+| **-- addref** [h k l] | add reflection with current position and energy |
+| {'tag'} | |
++-----------------------------+---------------------------------------------------+
+| **-- addref** [h k l] (p1, | add arbitrary reflection |
+| .., pN) energy {'tag'} | |
++-----------------------------+---------------------------------------------------+
+| **-- editref** num | interactively edit a reflection |
++-----------------------------+---------------------------------------------------+
+| **-- delref** num | deletes a reflection (numbered from 1) |
++-----------------------------+---------------------------------------------------+
+| **-- clearref** | deletes all the reflections |
++-----------------------------+---------------------------------------------------+
+| **-- swapref** | swaps first two reflections used for calculating |
+| | U matrix |
++-----------------------------+---------------------------------------------------+
+| **-- swapref** num1 num2 | swaps two reflections (numbered from 1) |
++-----------------------------+---------------------------------------------------+
+| **CRYSTAL ORIENTATIONS** |
++-----------------------------+---------------------------------------------------+
+| **-- showorient** | shows full list of crystal orientations |
++-----------------------------+---------------------------------------------------+
+| **-- addorient** | add crystal orientation interactively |
++-----------------------------+---------------------------------------------------+
+| **-- addorient** [h k l] | add crystal orientation in laboratory frame |
+| [x y z] {'tag'} | |
++-----------------------------+---------------------------------------------------+
+| **-- editorient** num | interactively edit a crystal orientation |
++-----------------------------+---------------------------------------------------+
+| **-- delorient** num | deletes a crystal orientation (numbered from 1) |
++-----------------------------+---------------------------------------------------+
+| **-- clearorient** | deletes all the crystal orientations |
++-----------------------------+---------------------------------------------------+
+| **-- swaporient** | swaps first two crystal orientations used for |
+| | calculating U matrix |
++-----------------------------+---------------------------------------------------+
+| **-- swaporient** num1 num2 | swaps two crystal orientations (numbered from 1) |
++-----------------------------+---------------------------------------------------+
+| **UB MATRIX** |
++-----------------------------+---------------------------------------------------+
+| **-- checkub** | show calculated and entered hkl values for |
+| | reflections |
++-----------------------------+---------------------------------------------------+
+| **-- setu** | manually set u matrix |
+| {[[..][..][..]]} | |
++-----------------------------+---------------------------------------------------+
+| **-- setub** | manually set ub matrix |
+| {[[..][..][..]]} | |
++-----------------------------+---------------------------------------------------+
+| **-- calcub** | (re)calculate u matrix from ref1 and ref2 |
++-----------------------------+---------------------------------------------------+
+| **-- trialub** | (re)calculate u matrix from ref1 only (check |
+| | carefully) |
++-----------------------------+---------------------------------------------------+
+| **-- refineub** {[h k l]} | refine unit cell dimensions and U matrix to match |
+| {pos} | diffractometer angles for a given hkl value |
++-----------------------------+---------------------------------------------------+
+| **-- addmiscut** angle | apply miscut to U matrix using a specified miscut |
+| {[x y z]} | angle in degrees and a rotation axis |
+| | (default: [0 1 0]) |
++-----------------------------+---------------------------------------------------+
+| **-- setmiscut** angle | manually set U matrix using a specified miscut |
+| {[x y z]} | angle in degrees and a rotation axis |
+| | (default: [0 1 0]) |
++-----------------------------+---------------------------------------------------+
+
+Motion Commands
+---------------
+
++-----------------------------+---------------------------------------------------+
+| **CONSTRAINTS** |
++-----------------------------+---------------------------------------------------+
+| **-- con** | list available constraints and values |
++-----------------------------+---------------------------------------------------+
+| **-- con** {val} | constrains and optionally sets one constraint |
++-----------------------------+---------------------------------------------------+
+| **-- con** {val} | clears and then fully constrains |
+| {val} {val} | |
++-----------------------------+---------------------------------------------------+
+| **-- uncon** | remove constraint |
++-----------------------------+---------------------------------------------------+
+| **HKL** |
++-----------------------------+---------------------------------------------------+
+| **-- allhkl** [h k l] | print all hkl solutions ignoring limits |
++-----------------------------+---------------------------------------------------+
+| **HARDWARE** |
++-----------------------------+---------------------------------------------------+
+| **-- hardware** | show diffcalc limits and cuts |
++-----------------------------+---------------------------------------------------+
+| **-- setcut** {name {val}} | sets cut angle |
++-----------------------------+---------------------------------------------------+
+| **-- setmin** {axis {val}} | set lower limits used by auto sector code (None |
+| | to clear) |
++-----------------------------+---------------------------------------------------+
+| **-- setmax** {name {val}} | sets upper limits used by auto sector code (None |
+| | to clear) |
++-----------------------------+---------------------------------------------------+
+| **MOTION** |
++-----------------------------+---------------------------------------------------+
+| **-- sim** hkl scn | simulates moving scannable (not all) |
++-----------------------------+---------------------------------------------------+
+| **-- sixc** | show Eularian position |
++-----------------------------+---------------------------------------------------+
+| **-- pos** sixc [mu, delta, | move to Eularian position(None holds an axis |
+| gam, eta, chi, phi] | still) |
++-----------------------------+---------------------------------------------------+
+| **-- sim** sixc [mu, delta, | simulate move to Eulerian positionsixc |
+| gam, eta, chi, phi] | |
++-----------------------------+---------------------------------------------------+
+| **-- hkl** | show hkl position |
++-----------------------------+---------------------------------------------------+
+| **-- pos** hkl [h k l] | move to hkl position |
++-----------------------------+---------------------------------------------------+
+| **-- pos** {h | k | l} val | move h, k or l to val |
++-----------------------------+---------------------------------------------------+
+| **-- sim** hkl [h k l] | simulate move to hkl position |
++-----------------------------+---------------------------------------------------+
+
+
+References
+----------
+
+.. [You1999] H. You. *Angle calculations for a '4S+2D' six-circle diffractometer.*
+ J. Appl. Cryst. (1999). **32**, 614-623. `(pdf link)
+ `__.
+
+.. [Busing1967] W. R. Busing and H. A. Levy. *Angle calculations for 3- and 4-circle X-ray
+ and neutron diffractometers.* Acta Cryst. (1967). **22**, 457-464. `(pdf link)
+ `__.
+
+.. [Vlieg1993] Martin Lohmeier and Elias Vlieg. *Angle calculations for a six-circle
+ surface x-ray diffractometer.* J. Appl. Cryst. (1993). **26**, 706-716. `(pdf link)
+ `__.
+
+.. [Vlieg1998] Elias Vlieg. *A (2+3)-type surface diffractometer: mergence of the z-axis and
+ (2+2)-type geometries.* J. Appl. Cryst. (1998). **31**, 198-203. `(pdf link)
+ `__.
+
+.. [Willmott2011] C. M. Schlepütz, S. O. Mariager, S. A. Pauli, R. Feidenhans'l and
+ P. R. Willmott. *Angle calculations for a (2+3)-type diffractometer: focus
+ on area detectors.* J. Appl. Cryst. (2011). **44**, 73-83. `(pdf link)
+ `__.
diff --git a/script/__Lib/diffcalc-2.1/README_template.rst b/script/__Lib/diffcalc-2.1/README_template.rst
new file mode 100755
index 0000000..f62cc5f
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/README_template.rst
@@ -0,0 +1,267 @@
+Diffcalc - A Diffraction Condition Calculator for Diffractometer Control
+========================================================================
+
+Diffcalc is a python/jython based diffraction condition calculator used for
+controlling diffractometers within reciprocal lattice space. It performs the
+same task as the fourc, sixc, twoc, kappa, psic and surf macros from SPEC.
+
+There is a `user guide `_ and `developer guide `_, both at `diffcalc.readthedocs.io `_
+
+|Travis| |Read the docs|
+
+.. |Travis| image:: https://travis-ci.org/DiamondLightSource/diffcalc.svg?branch=master
+ :target: https://travis-ci.org/DiamondLightSource/diffcalc
+ :alt: Build Status
+
+.. |Read the docs| image:: https://readthedocs.org/projects/diffcalc/badge/?version=latest
+ :target: http://diffcalc.readthedocs.io/en/latest/?badge=latest
+ :alt: Documentation Status
+
+.. contents::
+
+.. section-numbering::
+
+Software compatibility
+----------------------
+
+- Written in Python using numpy
+- Works in Jython using Jama
+- Runs directly in `OpenGDA`
+- Runs in in Python or IPython using minimal OpenGda emulation (included)
+- Contact us for help running in your environment
+
+Diffractometer compatibility
+----------------------------
+
+Diffcalc’s standard calculation engine is an implementation of [You1999]_ and
+[Busing1967]_. Diffcalc works with any diffractometer which is a subset of:
+
+ .. image:: https://raw.githubusercontent.com/DiamondLightSource/diffcalc/master/doc/source/youmanual_images/4s_2d_diffractometer.png
+ :alt: 4s + 2d six-circle diffractometer, from H.You (1999)
+ :width: 50%
+ :align: center
+
+Diffcalc can be configured to work with any diffractometer geometry which is a
+subset of this. For example, a five-circle diffractometer might be missing the
+nu circle above.
+
+Note that the first versions of Diffcalc were based on [Vlieg1993]_ and
+[Vlieg1998]_ and a ‘Vlieg’ engine is still available. There is also an engine
+based on [Willmott2011]_. The ‘You’ engine is more generic and the plan is to
+remove the old ‘Vlieg’ engine once beamlines have been migrated.
+
+If we choose the x axis parallel to b, the yaxis intheplaneofblandb2,andthezaxis perpendicular to that plane,
+
+Installation
+------------
+
+Check it out::
+
+ $ git clone https://github.com/DiamondLightSource/diffcalc.git
+ Cloning into 'diffcalc'...
+
+At Diamond Diffcalc may be installed within an OpenGDA deployment and is
+available via the 'module' system from bash.
+
+Starting
+--------
+
+Start diffcalc in ipython using a sixcircle dummy diffractometer::
+
+ $ cd diffcalc
+ $ ./diffcalc.py --help
+ ...
+
+ $ ./diffcalc.py sixcircle
+
+ Running: "ipython --no-banner --HistoryManager.hist_file=/tmp/ipython_hist_zrb13439.sqlite -i -m diffcmd.start sixcircle False"
+
+ ---------------------------------- DIFFCALC -----------------------------------
+ Startup script: '/Users/zrb13439/git/diffcalc/startup/sixcircle.py'
+ Loading ub calculation: 'test'
+ ------------------------------------ Help -------------------------------------
+ Quick: https://github.com/DiamondLightSource/diffcalc/blob/master/README.rst
+ Manual: https://diffcalc.readthedocs.io
+ Type: > help ub
+ > help hkl
+ -------------------------------------------------------------------------------
+ In [1]:
+
+Within Diamond use::
+
+ $ module load diffcalc
+ $ diffcalc --help
+ ...
+ $ diffcalc sixcircle
+
+Trying it out
+-------------
+
+Type ``demo.all()`` to see it working and then move try the following quick
+start guide::
+
+ >>> demo.all()
+ ...
+
+Getting help
+------------
+
+To view help with orientation and then moving in hkl space::
+
+ >>> help ub
+ ...
+ >>> help hkl
+ ...
+
+Configuring a UB calculation
+----------------------------
+See the full `user manual for many more
+options and an explanation of what this all means.
+
+To load the last used UB-calculation::
+
+ >>> lastub
+ Loading ub calculation: 'mono-Si'
+
+To load a previous UB-calculation::
+
+ >>> listub
+ UB calculations in: /Users/walton/.diffcalc/i16
+
+ 0) mono-Si 15 Feb 2017 (22:32)
+ 1) i16-32 13 Feb 2017 (18:32)
+
+ >>> loadub 0
+
+To create a new UB-calculation::
+
+ ==> newub 'example'
+ ==> setlat '1Acube' 1 1 1 90 90 90
+
+where the basis is defined by Busing & Levy:
+
+ "...we choose the x axis parallel to b, the y axis in the plane of bl
+ and b2, and the zaxis perpendicular to that plane."
+
+
+Find U matrix from two reflections::
+
+ ==> pos wl 1
+ ==> c2th [0 0 1]
+ 59.99999999999999
+
+ ==> pos sixc [0 60 0 30 90 0]
+ ==> addref [0 0 1]
+
+ ==> pos sixc [0 90 0 45 45 90]
+ ==> addref [0 1 1]
+
+
+Check that it looks good::
+
+ ==> checkub
+
+To see the resulting UB-calculation::
+
+ ==> ub
+
+Setting the reference vector
+----------------------------
+See the full `user manual for many more
+options and an explanation of what this all means.
+
+By default the reference vector is set parallel to the phi axis. That is,
+along the z-axis of the phi coordinate frame.
+
+The `ub` command shows the current reference vector, along with any inferred
+miscut, at the top its report (or it can be shown by calling ``setnphi`` or
+``setnhkl'`` with no args)::
+
+ >>> ub
+ ...
+ n_phi: 0.00000 0.00000 1.00000 <- set
+ n_hkl: -0.00000 0.00000 1.00000
+ miscut: None
+ ...
+
+Constraining solutions for moving in hkl space
+----------------------------------------------
+See the full `user manual for many more
+options and an explanation of what this all means.
+
+To get help and see current constraints::
+
+ >>> help con
+ ...
+
+ ==> con
+
+Three constraints can be given: zero or one from the DET and REF columns and the
+remainder from the SAMP column. Not all combinations are currently available.
+Use ``help con`` to see a summary if you run into troubles.
+
+To configure four-circle vertical scattering::
+
+ ==> con gam 0 mu 0 a_eq_b
+
+Moving in hkl space
+-------------------
+
+Simulate moving to a reflection::
+
+ ==> sim hkl [0 1 1]
+
+Move to reflection::
+
+ ==> pos hkl [0 1 1]
+
+ ==> pos sixc
+
+
+Scanning in hkl space
+---------------------
+
+Scan an hkl axis (and read back settings)::
+
+ ==> scan l 0 1 .2 sixc
+
+Scan a constraint (and read back virtual angles and eta)::
+
+ ==> con psi
+ ==> scan psi 70 110 10 hklverbose [0 1 1] eta
+
+
+Orientation Commands
+--------------------
+
+==> UB_HELP_TABLE
+
+Motion Commands
+---------------
+
+==> HKL_HELP_TABLE
+
+
+References
+----------
+
+.. [You1999] H. You. *Angle calculations for a '4S+2D' six-circle diffractometer.*
+ J. Appl. Cryst. (1999). **32**, 614-623. `(pdf link)
+ `__.
+
+.. [Busing1967] W. R. Busing and H. A. Levy. *Angle calculations for 3- and 4-circle X-ray
+ and neutron diffractometers.* Acta Cryst. (1967). **22**, 457-464. `(pdf link)
+ `__.
+
+.. [Vlieg1993] Martin Lohmeier and Elias Vlieg. *Angle calculations for a six-circle
+ surface x-ray diffractometer.* J. Appl. Cryst. (1993). **26**, 706-716. `(pdf link)
+ `__.
+
+.. [Vlieg1998] Elias Vlieg. *A (2+3)-type surface diffractometer: mergence of the z-axis and
+ (2+2)-type geometries.* J. Appl. Cryst. (1998). **31**, 198-203. `(pdf link)
+ `__.
+
+.. [Willmott2011] C. M. Schlepütz, S. O. Mariager, S. A. Pauli, R. Feidenhans'l and
+ P. R. Willmott. *Angle calculations for a (2+3)-type diffractometer: focus
+ on area detectors.* J. Appl. Cryst. (2011). **44**, 73-83. `(pdf link)
+ `__.
diff --git a/script/__Lib/diffcalc-2.1/buckminster.cspec b/script/__Lib/diffcalc-2.1/buckminster.cspec
new file mode 100755
index 0000000..745b4c3
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/buckminster.cspec
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/script/__Lib/diffcalc-2.1/diffcalc.py b/script/__Lib/diffcalc-2.1/diffcalc.py
new file mode 100755
index 0000000..66be493
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc.py
@@ -0,0 +1,9 @@
+#!/usr/bin/python
+
+import sys
+
+from diffcmd.diffcalc_launcher import main
+
+
+if __name__ == '__main__':
+ sys.exit(main())
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/__init__.py b/script/__Lib/diffcalc-2.1/diffcalc/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/dc/__init__.py b/script/__Lib/diffcalc-2.1/diffcalc/dc/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/dc/common.py b/script/__Lib/diffcalc-2.1/diffcalc/dc/common.py
new file mode 100755
index 0000000..9793f71
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/dc/common.py
@@ -0,0 +1,24 @@
+from diffcalc.util import allnum, command, DiffcalcException
+
+
+def sim(scn, hkl):
+ """sim hkl scn -- simulates moving scannable (not all)
+ """
+ if not isinstance(hkl, (tuple, list)):
+ raise TypeError()
+
+ if not allnum(hkl):
+ raise TypeError()
+
+ try:
+ print scn.simulateMoveTo(hkl)
+ except AttributeError:
+ raise TypeError(
+ "The first argument does not support simulated moves")
+
+def energy_to_wavelength(energy):
+ try:
+ return 12.39842 / energy
+ except ZeroDivisionError:
+ raise DiffcalcException(
+ "Cannot calculate hkl position as Energy is set to 0")
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/dc/dcvlieg.py b/script/__Lib/diffcalc-2.1/diffcalc/dc/dcvlieg.py
new file mode 100755
index 0000000..b1fb5da
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/dc/dcvlieg.py
@@ -0,0 +1,76 @@
+from diffcalc.dc.common import energy_to_wavelength
+
+from diffcalc import settings
+from diffcalc.hkl.vlieg.transform import VliegTransformSelector,\
+ TransformCommands, VliegPositionTransformer
+from diffcalc.dc.help import compile_extra_motion_commands_for_help
+import diffcalc.hkl.vlieg.calc
+
+
+# reload to aid testing only
+import diffcalc.ub.ub as _ub
+reload(_ub)
+from diffcalc import hardware as _hardware
+#reload(_hardware)
+import diffcalc.hkl.vlieg.hkl as _hkl
+reload(_hkl)
+
+from diffcalc.ub.ub import * # @UnusedWildImport
+from diffcalc.hardware import * # @UnusedWildImport
+from diffcalc.hkl.vlieg.hkl import * # @UnusedWildImport
+from diffcalc.gdasupport.scannable.sim import sim
+
+_transform_selector = VliegTransformSelector()
+_transform_commands = TransformCommands(_transform_selector)
+_transformer = VliegPositionTransformer(settings.geometry, settings.hardware,
+ _transform_selector)
+
+transform = _transform_commands.transform
+transforma = _transform_commands.transforma
+transformb = _transform_commands.transformb
+transformc = _transform_commands.transformc
+
+
+on = 'on'
+off = 'off'
+auto = 'auto'
+manual = 'manual'
+
+def hkl_to_angles(h, k, l, energy=None):
+ """Convert a given hkl vector to a set of diffractometer angles"""
+ if energy is None:
+ energy = settings.hardware.get_energy() # @UndefinedVariable
+
+ position, params = hklcalc.hklToAngles(h, k, l, energy_to_wavelength(energy))
+ position = _transformer.transform(position)
+ angle_tuple = settings.geometry.internal_position_to_physical_angles(position) # @UndefinedVariable
+ angle_tuple = settings.hardware.cut_angles(angle_tuple) # @UndefinedVariable
+
+ return angle_tuple, params
+
+
+def angles_to_hkl(angleTuple, energy=None):
+ """Converts a set of diffractometer angles to an hkl position
+ ((h, k, l), paramDict)=angles_to_hkl(self, (a1, a2,aN), energy=None)"""
+ if energy is None:
+ energy = settings.hardware.get_energy() # @UndefinedVariable
+
+ i_pos = settings.geometry.physical_angles_to_internal_position(angleTuple) # @UndefinedVariable
+ return hklcalc.anglesToHkl(i_pos, energy_to_wavelength(energy))
+
+
+settings.ubcalc_strategy = diffcalc.hkl.vlieg.calc.VliegUbCalcStrategy()
+settings.angles_to_hkl_function = diffcalc.hkl.vlieg.calc.vliegAnglesToHkl
+settings.include_sigtau = True
+
+ub_commands_for_help = _ub.commands_for_help
+
+hkl_commands_for_help = (_hkl.commands_for_help +
+ _hardware.commands_for_help +
+ ['Transform',
+ transform,
+ transforma,
+ transformb,
+ transformc] +
+ compile_extra_motion_commands_for_help())
+
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/dc/dcwillmot.py b/script/__Lib/diffcalc-2.1/diffcalc/dc/dcwillmot.py
new file mode 100755
index 0000000..7bd8a87
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/dc/dcwillmot.py
@@ -0,0 +1,47 @@
+# This file differs from dcyou in only two places
+
+from diffcalc import settings
+from diffcalc.dc.common import energy_to_wavelength
+from diffcalc.dc.help import compile_extra_motion_commands_for_help
+import diffcalc.hkl.willmott.calc
+
+
+# reload to aid testing only
+from diffcalc.ub import ub as _ub
+reload(_ub)
+from diffcalc import hardware as _hardware
+#reload(_hardware)
+from diffcalc.hkl.you import hkl as _hkl
+reload(_hkl)
+
+from diffcalc.ub.ub import * # @UnusedWildImport
+from diffcalc.hardware import * # @UnusedWildImport
+from diffcalc.hkl.willmot.hkl import * # @UnusedWildImport
+from diffcalc.gdasupport.scannable.sim import sim
+
+def hkl_to_angles(h, k, l, energy=None):
+ """Convert a given hkl vector to a set of diffractometer angles"""
+ if energy is None:
+ energy = settings.hardware.get_energy() # @UndefinedVariable
+
+ (pos, params) = hklcalc.hklToAngles(h, k, l, energy_to_wavelength(energy))
+ angle_tuple = settings.geometry.internal_position_to_physical_angles(pos) # @UndefinedVariable
+ angle_tuple = settings.hardware.cut_angles(angle_tuple) # @UndefinedVariable
+
+ return angle_tuple, params
+
+def angles_to_hkl(angleTuple, energy=None):
+ """Converts a set of diffractometer angles to an hkl position
+ ((h, k, l), paramDict)=angles_to_hkl(self, (a1, a2,aN), energy=None)"""
+ if energy is None:
+ energy = settings.hardware.get_energy() # @UndefinedVariable
+ i_pos = settings.geometry.physical_angles_to_internal_position(angleTuple) # @UndefinedVariable
+ return hklcalc.anglesToHkl(i_pos, energy_to_wavelength(energy))
+
+settings.ubcalc_strategy = diffcalc.hkl.willmott.calc.WillmottHorizontalUbCalcStrategy()
+settings.angles_to_hkl_function = diffcalc.hkl.willmott.calc.angles_to_hkl
+
+
+ub_commands_for_help = _ub.commands_for_help
+
+hkl_commands_for_help = _hkl.commands_for_help + _hardware.commands_for_help + compile_extra_motion_commands_for_help()
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/dc/dcyou.py b/script/__Lib/diffcalc-2.1/diffcalc/dc/dcyou.py
new file mode 100755
index 0000000..651c2fe
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/dc/dcyou.py
@@ -0,0 +1,56 @@
+from diffcalc import settings
+from diffcalc.dc.common import energy_to_wavelength
+from diffcalc.dc.help import compile_extra_motion_commands_for_help
+
+import diffcalc.hkl.you.calc
+settings.ubcalc_strategy = diffcalc.hkl.you.calc.YouUbCalcStrategy()
+settings.angles_to_hkl_function = diffcalc.hkl.you.calc.youAnglesToHkl
+settings.include_reference = True
+
+# reload to aid testing only
+from diffcalc.ub import ub as _ub
+
+reload(_ub)
+from diffcalc import hardware as _hardware
+#reload(_hardware)
+from diffcalc.hkl.you import hkl as _hkl
+reload(_hkl)
+
+from diffcalc.ub.ub import * # @UnusedWildImport
+from diffcalc.hardware import * # @UnusedWildImport
+from diffcalc.hkl.you.hkl import * # @UnusedWildImport
+
+
+def hkl_to_angles(h, k, l, energy=None):
+ """Convert a given hkl vector to a set of diffractometer angles
+
+ return angle tuple and params dictionary
+
+ """
+ if energy is None:
+ energy = settings.hardware.get_energy() # @UndefinedVariable
+
+ (pos, params) = hklcalc.hklToAngles(h, k, l, energy_to_wavelength(energy))
+ angle_tuple = settings.geometry.internal_position_to_physical_angles(pos) # @UndefinedVariable
+ angle_tuple = settings.hardware.cut_angles(angle_tuple) # @UndefinedVariable
+
+ return angle_tuple, params
+
+
+def angles_to_hkl(angleTuple, energy=None):
+ """Converts a set of diffractometer angles to an hkl position
+
+ Return hkl tuple and params dictionary
+
+ """
+ if energy is None:
+ energy = settings.hardware.get_energy() # @UndefinedVariable
+ i_pos = settings.geometry.physical_angles_to_internal_position(angleTuple) # @UndefinedVariable
+ return hklcalc.anglesToHkl(i_pos, energy_to_wavelength(energy))
+
+
+
+
+
+ub_commands_for_help = _ub.commands_for_help
+hkl_commands_for_help = _hkl.commands_for_help + _hardware.commands_for_help + compile_extra_motion_commands_for_help()
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/dc/help.py b/script/__Lib/diffcalc-2.1/diffcalc/dc/help.py
new file mode 100755
index 0000000..5d4afe6
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/dc/help.py
@@ -0,0 +1,161 @@
+'''
+Created on 6 May 2016
+
+@author: walton
+'''
+from diffcalc import settings
+from diffcalc.gdasupport.scannable.sim import sim
+import textwrap
+from diffcalc.util import bold
+
+
+class ExternalCommand(object):
+ """Instances found in a command_list by format_command_help will
+ result in documentation for a command without there actually being one.
+ """
+ def __init__(self, docstring):
+ """Set the docstring that will be pulled off by format_command_help.
+ """
+ self.__doc__ = docstring
+ self.__name__ = ''
+
+
+WIDTH = 27
+INDENT = 3
+
+
+def format_command_help(command_list):
+
+ row_list = _command_list_to_table_cells(command_list)
+ lines = []
+ for row_cells in row_list:
+ if len(row_cells) == 1:
+ heading = row_cells[0]
+ lines.append('')
+ lines.append(bold(heading))
+ lines.append('')
+ elif len(row_cells) == 2:
+ cell1, cell2 = row_cells
+
+ cell1_lines = textwrap.wrap(cell1, WIDTH, subsequent_indent=' ')
+ cell2_lines = textwrap.wrap(cell2, 79 - INDENT - 3 - WIDTH)
+
+ first_line = True
+ while cell1_lines or cell2_lines:
+ line = ' ' * INDENT
+ if cell1_lines:
+ line += cell1_lines.pop(0).ljust(WIDTH)
+ else:
+ line += ' ' * (WIDTH)
+ line += ' : ' if first_line else ' '
+ if cell2_lines:
+ line += cell2_lines.pop(0)
+ lines.append(line)
+ first_line = False
+
+ return '\n'.join(lines)
+
+
+def format_commands_for_rst_table(title, command_list):
+ W1 = WIDTH # internal width
+ W2 = 79 - W1 - 3 # internal width
+ HORIZ_LINE = '+-' + '-' * W1 + '-+-' + '-' * W2 + '-+'
+
+ row_list = _command_list_to_table_cells(command_list)
+
+ lines = []
+
+ lines.append(HORIZ_LINE) # Top line
+ for row_cells in row_list:
+ if len(row_cells) == 1:
+ lines.append('| ' + ('**' + row_cells[0] + '**').ljust(W1 + W2 + 3) + ' |')
+
+ elif len(row_cells) == 2:
+ cmd_and_args = row_cells[0].split(' ', 1)
+ cmd = cmd_and_args[0]
+ args = cmd_and_args[1] if len(cmd_and_args) == 2 else ''
+ cell1 = '**-- %s** %s' % (cmd, args)
+ cell1_lines = textwrap.wrap(cell1, W1) #, subsequent_indent=' ')
+ cell2_lines = textwrap.wrap(row_cells[1], W2)
+
+ while cell1_lines or cell2_lines:
+ line = '| '
+ line += (cell1_lines.pop(0) if cell1_lines else '').ljust(W1)
+ line += ' | '
+ line += (cell2_lines.pop(0) if cell2_lines else '').ljust(W2)
+ line += ' |'
+ lines.append(line)
+
+ else:
+ assert False
+
+ lines.append(HORIZ_LINE)
+ return lines
+
+
+
+
+
+def _command_list_to_table_cells(command_list):
+ row_list = []
+ for obj in command_list:
+
+ if isinstance(obj, basestring): # group heading
+ row_list.append([obj.upper()])
+
+ else: # individual command
+ doc_before_empty_line = obj.__doc__.split('\n\n')[0]
+ doc_lines = [s.strip() for s in doc_before_empty_line.split('\n')]
+ for doc_line in doc_lines:
+ if doc_line == '':
+ continue
+ if obj.__name__ in ('ub', 'hkl'):
+ continue
+ name, args, desc = _split_doc_line(doc_line)
+ desc = desc.strip()
+ args = args.strip()
+ if desc and desc[-1] == '.':
+ desc = desc[:-1]
+
+ row_list.append([name + (' ' if args else '') + args, desc])
+
+ return row_list
+
+
+def _split_doc_line(docLine):
+ name, _, right = docLine.partition(' ')
+ args, _, desc = right.partition('-- ')
+ return name, args, desc
+
+
+def compile_extra_motion_commands_for_help():
+
+ _hwname = settings.hardware.name # @UndefinedVariable
+ _angles = ', '.join(settings.hardware.get_axes_names()) # @UndefinedVariable
+
+ commands = []
+
+ commands.append('Motion')
+ commands.append(sim)
+ commands.append(ExternalCommand(
+ '%(_hwname)s -- show Eularian position' % vars()))
+ commands.append(ExternalCommand(
+ 'pos %(_hwname)s [%(_angles)s] -- move to Eularian position'
+ '(None holds an axis still)' % vars()))
+ commands.append(ExternalCommand(
+ 'sim %(_hwname)s [%(_angles)s] -- simulate move to Eulerian position'
+ '%(_hwname)s' % vars()))
+
+ commands.append(ExternalCommand(
+ 'hkl -- show hkl position'))
+ commands.append(ExternalCommand(
+ 'pos hkl [h k l] -- move to hkl position'))
+ commands.append(ExternalCommand(
+ 'pos {h | k | l} val -- move h, k or l to val'))
+ commands.append(ExternalCommand(
+ 'sim hkl [h k l] -- simulate move to hkl position'))
+
+# if engine_name != 'vlieg':
+# pass
+# # TODO: remove sigtau command and 'Surface' string
+ return commands
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/__init__.py b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/minigda/__init__.py b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/minigda/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/minigda/command.py b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/minigda/command.py
new file mode 100755
index 0000000..d6e7fc5
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/minigda/command.py
@@ -0,0 +1,322 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+#try:
+# from gda.device import Scannable
+#except ImportError:
+# from diffcalc.gdasupport.minigda.scannable import Scannable
+from diffcalc.gdasupport.minigda.scannable import Scannable
+from diffcalc.util import getMessageFromException, allnum, bold
+import math
+
+
+ROOT_NAMESPACE_DICT = {}
+
+class Pos(object):
+
+ def __init__(self):
+ self.__name__ = 'pos'
+
+ def __call__(self, *posargs):
+ if len(posargs) == 0:
+
+ keys = dict(ROOT_NAMESPACE_DICT).keys()
+ keys.sort()
+ for key in keys:
+ val = ROOT_NAMESPACE_DICT[key]
+ if isinstance(val, Scannable):
+ print self.posReturningReport(val)
+ else:
+ print self.posReturningReport(*posargs)
+
+ def posReturningReport(self, *posargs):
+ # report position of this scannable
+ if len(posargs) == 1:
+ scannable = posargs[0]
+ self._assert_scannable(scannable)
+ return self._generatePositionReport(scannable)
+
+ # Move the scannable and report
+ elif len(posargs) == 2:
+ scannable = posargs[0]
+ self._assert_scannable(scannable)
+ # Move it
+ scannable.asynchronousMoveTo(posargs[1])
+ # TODO: minigda assumes all moves complete instantly, so no need
+ # yet to check the move is complete
+ return self._generatePositionReport(scannable)
+
+ else:
+ raise ValueError(
+ "Invlaid arguements: 'pos [ scannable [ value ] ]'")
+
+ def _assert_scannable(self, obj):
+ if not isinstance(obj, Scannable):
+ raise TypeError(
+ "The first argument to the pos command must be scannable. "
+ "Not: " + str(type(obj)))
+
+ def _generatePositionReport(self, scannable):
+ fieldNames = (tuple(scannable.getInputNames()) +
+ tuple(scannable.getExtraNames()))
+ # All scannables
+ result = "%s:" % scannable.getName()
+ result = result.ljust(10)
+ try:
+ pos = scannable.getPosition()
+ except Exception, e:
+ return result + "Error: %s" % getMessageFromException(e)
+ if pos is None:
+ return result + "---"
+ # Single field scannable:
+ if len(fieldNames) == 1:
+ try:
+ result += "%s" % scannable.formatPositionFields(pos)[0]
+ except AttributeError:
+ result += str(scannable())
+ # Multi field scannable:
+ else:
+ try:
+ formatted = scannable.formatPositionFields(pos)
+ for name, formattedValue in zip(fieldNames, formatted):
+ result += "%s: %s " % (name, formattedValue)
+ except AttributeError:
+ result += str(scannable())
+
+ return result
+
+
+class ScanDataHandler:
+ def __init__(self):
+ self.scannables = None
+
+ def callAtScanStart(self, scannables):
+ pass
+
+ def callWithScanPoint(self, PositionDictIndexedByScannable):
+ pass
+
+ def callAtScanEnd(self):
+ pass
+
+
+class ScanDataPrinter(ScanDataHandler):
+
+ def __init__(self):
+ self.first_point_printed = False
+ self.widths = []
+ self.scannables = []
+
+ def callAtScanStart(self, scannables):
+ self.first_point_printed = False
+ self.scannables = scannables
+
+ def print_first_point(self, position_dict):
+ # also sets self.widths
+ header_strings = []
+ for scn in self.scannables:
+ field_names = list(scn.getInputNames()) + list(scn.getExtraNames())
+ if len(field_names) == 1:
+ header_strings.append(scn.getName())
+ else:
+ for field_name in field_names:
+ header_strings.append(field_name)
+
+ first_row_strings = []
+ for scn in self.scannables:
+ pos = position_dict[scn]
+ first_row_strings.extend(scn.formatPositionFields(pos))
+
+ self.widths = []
+ for header, pos_string in zip(header_strings, first_row_strings):
+ self.widths.append(max(len(header), len(pos_string)))
+
+ header_cells = []
+ for heading, width in zip(header_strings, self.widths):
+ header_cells.append(heading.rjust(width))
+
+ underline_cells = ['-' * w for w in self.widths]
+
+ first_row_cells = []
+ for pos, width in zip(first_row_strings, self.widths):
+ first_row_cells.append(pos.rjust(width))
+
+ #table_width = sum(self.widths) + len(self.widths * 2) - 2
+ lines = []
+ #lines.append('=' * table_width)
+ lines.append(bold(' '.join(header_cells)))
+ lines.append(' '.join(underline_cells))
+ lines.append(' '.join(first_row_cells))
+ print '\n'.join(lines)
+
+ def callWithScanPoint(self, position_dict):
+ if not self.first_point_printed:
+ self.print_first_point(position_dict)
+ self.first_point_printed = True
+ else:
+ row_strings = []
+ for scn in self.scannables:
+ pos = position_dict[scn]
+ row_strings.extend(scn.formatPositionFields(pos))
+
+ row_cells = []
+ for pos, width in zip(row_strings, self.widths):
+ row_cells.append(pos.rjust(width))
+
+ print ' '.join(row_cells)
+
+ def callAtScanEnd(self):
+ #table_width = sum(self.widths) + len(self.widths * 2) - 2
+ #print '=' * table_width
+ pass
+
+
+class Scan(object):
+ class Group:
+ def __init__(self, scannable):
+ self.scannable = scannable
+ self.args = []
+
+ def __cmp__(self, other):
+ return(self.scannable.getLevel() - other.scannable.getLevel())
+
+ def __repr__(self):
+ return "Group(%s, %s)" % (self.scannable.getName(), str(self.args))
+
+ def shouldTriggerLoop(self):
+ return len(self.args) == 3
+
+ def __init__(self, scanDataHandlers):
+ # scanDataHandlers should be list
+ if type(scanDataHandlers) not in (tuple, list):
+ scanDataHandlers = (scanDataHandlers,)
+ self.dataHandlers = scanDataHandlers
+
+ def __call__(self, *scanargs):
+ groups = self._parseScanArgsIntoScannableArgGroups(scanargs)
+ groups = self._reorderInnerGroupsAccordingToLevel(groups)
+ # Configure data handlers for a new scan
+ for handler in self.dataHandlers: handler.callAtScanStart(
+ [grp.scannable for grp in groups])
+ # Perform the scan
+ self._performScan(groups, currentRecursionLevel=0)
+ # Inform data handlers of scan completion
+ for handler in self.dataHandlers: handler.callAtScanEnd()
+
+ def _parseScanArgsIntoScannableArgGroups(self, scanargs):
+ """
+ -> [ Group(scnA, (a1, a2, a2)), Group((scnB), (b1)), ...
+ ... Group((scnC),()), Group((scnD),(d1))]
+ """
+ result = []
+ if not isinstance(scanargs[0], Scannable):
+ raise TypeError("First scan argument must be a scannable")
+
+ # Parse out scannables followed by non-scannable args
+ for arg in scanargs:
+ if isinstance(arg, Scannable):
+ result.append(Scan.Group(arg))
+ else:
+ result[-1].args.append(arg)
+ return result
+
+ def _reorderInnerGroupsAccordingToLevel(self, groups):
+ # Find the first group not to trigger a loop
+ for idx, group in enumerate(groups):
+ if not group.shouldTriggerLoop():
+ break
+ latter = groups[idx:]; latter.sort() # Horrible hack not needed in python 3!
+ return groups[:idx] + latter
+
+ def _performScan(self, groups, currentRecursionLevel):
+ # groups[currentRecursionLevel:] will start with either:
+ # a) A loop triggering group
+ # b) A number (possibly 0) of non-loop triggering groups
+ unprocessedGroups = groups[currentRecursionLevel:]
+
+ # 1) If first remaining group should trigger a loop, perform this loop,
+ # recursively calling this method on the remaining groups
+ if len(unprocessedGroups) > 0:
+ first = unprocessedGroups[0]
+ # If groups starts with a request to loop:
+ if first.shouldTriggerLoop():
+ posList = self._frange(first.args[0], first.args[1], first.args[2])
+ for pos in posList:
+ first.scannable.asynchronousMoveTo(pos)
+ # TODO: Should wait. minigda assumes all moves complete immediately
+ self._performScan(groups, currentRecursionLevel + 1)
+ return
+
+ # 2) Move all non-loop triggering groups (may be zero)
+ self._moveNonLoopTriggeringGroups(unprocessedGroups)
+
+ # 3) Sample position of all scannables
+ posDict = self._samplePositionsOfAllScannables(groups)
+
+ # 4) Inform the data handlers that this point has been recorded
+ for handler in self.dataHandlers: handler.callWithScanPoint(posDict)
+
+ def _moveNonLoopTriggeringGroups(self, groups):
+ # TODO: Should wait. minigda assumes all moves complete immediately. groups could be zero lengthed.
+ for grp in groups:
+ if len(grp.args) == 0:
+ pass
+ elif len(grp.args) == 1:
+ grp.scannable.asynchronousMoveTo(grp.args[0])
+ elif len(grp.args) == 2:
+ raise Exception("Scannables followed by two args not supported by minigda's scan command ")
+ else:
+ raise Exception("Scannable: %s args%s" % (grp.scannable, str(grp.args)))
+
+ def _samplePositionsOfAllScannables(self, groups):
+ posDict = {}
+ for grp in groups:
+ posDict[grp.scannable] = grp.scannable.getPosition()
+ return posDict
+
+ def _frange(self, limit1, limit2, increment):
+ """Range function that accepts floats (and integers).
+ """
+# limit1 = float(limit1)
+# limit2 = float(limit2)
+ try:
+ increment = float(increment)
+ except TypeError:
+ raise TypeError(
+ "Only scaler values are supported, not GDA format vectors.")
+ count = int(math.ceil(((limit2 - limit1) + increment / 100.) / increment))
+ result = []
+ for n in range(count):
+ result.append(limit1 + n * increment)
+ return result
+
+
+def sim(scn, hkl):
+ """sim hkl scn -- simulates moving scannable (not all)
+ """
+ if not isinstance(hkl, (tuple, list)):
+ raise TypeError()
+
+ if not allnum(hkl):
+ raise TypeError()
+
+ try:
+ print scn.simulateMoveTo(hkl)
+ except AttributeError:
+ raise TypeError(
+ "The first argument does not support simulated moves")
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/minigda/scannable.py b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/minigda/scannable.py
new file mode 100755
index 0000000..f6f9926
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/minigda/scannable.py
@@ -0,0 +1,511 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+import time
+
+try:
+ from gda.device.scannable import ScannableBase
+except ImportError:
+ class Scannable(object):
+ pass
+
+ class ScannableBase(Scannable):
+ """Implemtation of a subset of OpenGDA's Scannable interface
+ """
+
+ level = 5
+ inputNames = []
+ extraNames = []
+ outputFormat = []
+
+ def isBusy(self):
+ raise NotImplementedError()
+
+ def rawGetPosition(self):
+ raise NotImplementedError()
+
+ def rawAsynchronousMoveTo(self, newpos):
+ raise NotImplementedError()
+
+ def waitWhileBusy(self):
+ while self.isBusy():
+ time.sleep(.1)
+
+ def getPosition(self):
+ return self.rawGetPosition()
+
+ def asynchronousMoveTo(self, newpos):
+ self.rawAsynchronousMoveTo(newpos)
+
+ def atScanStart(self):
+ pass
+
+ def atScanEnd(self):
+ pass
+
+ def atCommandFailure(self):
+ pass
+
+ ###
+
+ def __repr__(self):
+ pos = self.getPosition()
+ formattedValues = self.formatPositionFields(pos)
+ if len(tuple(self.getInputNames()) + tuple(self.getExtraNames())) > 1:
+ result = self.getName() + ': '
+ else:
+ result = ''
+
+ names = tuple(self.getInputNames()) + tuple(self.getExtraNames())
+ for name, val in zip(names, formattedValues):
+ result += ' ' + name + ': ' + val
+ return result
+ ###
+
+ def formatPositionFields(self, pos):
+ """Returns position as array of formatted strings"""
+ # Make sure pos is a tuple or list
+ if type(pos) not in (tuple, list):
+ pos = tuple([pos])
+
+ # Sanity check
+ if len(pos) != len(self.getOutputFormat()):
+ raise Exception(
+ "In scannable '%s':number of position fields differs from "
+ "number format strings specified" % self.getName())
+
+ result = []
+ for field, format in zip(pos, self.getOutputFormat()):
+ if field is None:
+ result.append('???')
+ else:
+ s = (format % field)
+ ## if width!=None:
+ ## s = s.ljust(width)
+ result.append(s)
+
+ return result
+
+ def getName(self):
+ return self.name
+
+ def setName(self, value):
+ self.name = value
+
+ def getLevel(self):
+ return self.level
+
+ def setLevel(self, value):
+ self.level = value
+
+ def getInputNames(self):
+ return self.inputNames
+
+ def setInputNames(self, value):
+ self.inputNames = value
+
+ def getExtraNames(self):
+ return self.extraNames
+
+ def setExtraNames(self, value):
+ self.extraNames = value
+
+ def getOutputFormat(self):
+ return self.outputFormat
+
+ def setOutputFormat(self, value):
+ if type(value) not in (tuple, list):
+ raise TypeError(
+ "%s.setOutputFormat() expects tuple or list; not %s" %
+ (self.getName(), str(type(value))))
+ self.outputFormat = value
+
+ def __call__(self, newpos=None):
+ if newpos is None:
+ return self.getPosition()
+ self.asynchronousMoveTo(newpos)
+
+ class ScannableAdapter(Scannable):
+ '''Wrap up a Scannable and give it a new name and optionally an offset
+ (added to the delegate when reading up and subtracting when setting down
+ '''
+
+ def __init__(self, delegate_scn, name, offset=0):
+ assert len(delegate_scn.getInputNames()) == 1
+ assert len(delegate_scn.getExtraNames()) == 0
+ self.delegate_scn = delegate_scn
+ self.name = name
+ self.offset = offset
+
+ def __getattr__(self, name):
+ return getattr(self.delegate_scn, name)
+
+ def getName(self):
+ return self.name
+
+ def getInputNames(self):
+ return [self.name]
+
+ def getPosition(self):
+ return self.delegate_scn.getPosition() + self.offset
+
+ def asynchronousMoveTo(self, newpos):
+ self.delegate_scn.asynchronousMoveTo(newpos - self.offset)
+
+ def __repr__(self):
+ pos = self.getPosition()
+ formatted_values = self.delegate_scn.formatPositionFields(pos)
+ return self.name + ': ' + formatted_values[0] + ' ' + self.get_hint()
+
+ def get_hint(self):
+ if self.offset:
+ offset_hint = ' + ' if self.offset >= 0 else ' - '
+ offset_hint += str(self.offset)
+ else:
+ offset_hint = ''
+ return '(%s%s)' % (self.delegate_scn.name, offset_hint)
+
+ def __call__(self, newpos=None):
+ if newpos is None:
+ return self.getPosition()
+ self.asynchronousMoveTo(newpos)
+
+class SingleFieldDummyScannable(ScannableBase):
+
+ def __init__(self, name, initial_position=0.):
+ self.name = name
+ self.inputNames = [name]
+ self.outputFormat = ['% 6.4f']
+ self.level = 3
+ self._current_position = float(initial_position)
+
+ def isBusy(self):
+ return False
+
+ def waitWhileBusy(self):
+ return
+
+ def asynchronousMoveTo(self, new_position):
+ self._current_position = float(new_position)
+
+ def getPosition(self):
+ return self._current_position
+
+
+class DummyPD(SingleFieldDummyScannable):
+ """For compatability with the gda's dummy_pd module"""
+ pass
+
+
+class MultiInputExtraFieldsDummyScannable(ScannableBase):
+ '''Multi input Dummy PD Class supporting input and extra fields'''
+ def __init__(self, name, inputNames, extraNames):
+ self.setName(name)
+ self.setInputNames(inputNames)
+ self.setExtraNames(extraNames)
+ self.setOutputFormat(['%6.4f'] * (len(inputNames) + len(extraNames)))
+ self.setLevel(3)
+ self.currentposition = [0.0] * len(inputNames)
+
+ def isBusy(self):
+ return 0
+
+ def asynchronousMoveTo(self, new_position):
+ if type(new_position) == type(1) or type(new_position) == type(1.0):
+ new_position = [new_position]
+ msg = "Wrong new_position size"
+ assert len(new_position) == len(self.currentposition), msg
+ for i in range(len(new_position)):
+ if new_position[i] != None:
+ self.currentposition[i] = float(new_position[i])
+
+ def getPosition(self):
+ extraValues = range(100, 100 + (len(self.getExtraNames())))
+ return self.currentposition + map(float, extraValues)
+
+
+class ZeroInputExtraFieldsDummyScannable(ScannableBase):
+ '''Zero input/extra field dummy pd
+ '''
+ def __init__(self, name):
+ self.setName(name)
+ self.setInputNames([])
+ self.setOutputFormat([])
+
+ def isBusy(self):
+ return 0
+
+ def asynchronousMoveTo(self, new_position):
+ pass
+
+ def getPosition(self):
+ pass
+
+
+class ScannableGroup(ScannableBase):
+ """wraps up motors. Simulates motors if non given."""
+
+ def __init__(self, name, motorList):
+
+ self.setName(name)
+ # Set input format
+ motorNames = []
+ for scn in motorList:
+ motorNames.append(scn.getName())
+ self.setInputNames(motorNames)
+ # Set output format
+ format = []
+ for motor in motorList:
+ format.append(motor.getOutputFormat()[0])
+ self.setOutputFormat(format)
+ self.__motors = motorList
+
+ def asynchronousMoveTo(self, position):
+ # if input has any Nones, then replace these with the current positions
+ if None in position:
+ position = list(position)
+ current = self.getPosition()
+ for idx, val in enumerate(position):
+ if val is None:
+ position[idx] = current[idx]
+
+ for scn, pos in zip(self.__motors, position):
+ scn.asynchronousMoveTo(pos)
+
+ def getPosition(self):
+ return [scn.getPosition() for scn in self.__motors]
+
+ def isBusy(self):
+ for scn in self.__motors:
+ if scn.isBusy():
+ return True
+ return False
+
+ def configure(self):
+ pass
+
+
+class ScannableMotionWithScannableFieldsBase(ScannableBase):
+ '''
+ This extended version of ScannableMotionBase contains a
+ completeInstantiation() method which adds a dictionary of
+ MotionScannableParts to an instance. Each part allows one of the
+ instances fields to be interacted with like it itself is a scannable.
+ Fields are dynamically added to the instance linking to these parts
+ allowing dotted access from Jython. They may also be accessed using
+ Jython container access methods (via the __getitem__() method). To acess
+ them from Jave use the getComponent(name) method.
+
+ When moving a part (via either a pos or scan command), the part calls
+ the parent to perform the actual task. The parts asynchronousMoveto
+ command will call the parent with a list of None values except for the
+ field it represents which will be passed the desired position value.
+
+ The asynchronousMoveTo method in class that inherats from this base
+ class then must handle these Nones. In some cases the method may
+ actually be able to move the underlying system assoiciated with one
+ field individually from others. If this is not possible the best
+ behaviour may be to simply not support this beahviour and exception or
+ alternatively to substitute the None values with actual current position
+ of parent's scannables associated fields.
+
+ ScannableMotionBaseWithMemory() inherats from this calss and provides a
+ solution useful for some scenarious: it keeps track of the last position
+ moved to, and replaces the Nones in an asynchronousMoveTo request with
+ these values. There are a number of dangers associated with this which
+ are addressed in that class's documentation, but it provides a way to
+ move one axis within a group of non-orthogonal axis while keeping the
+ others still.
+ '''
+ childrenDict = {}
+ numInputFields = None
+ numExtraFields = None
+
+ def completeInstantiation(self):
+ '''This method should be called at the end of all user defined
+ consructors'''
+ # self.validate()
+ self.numInputFields = len(self.getInputNames())
+ self.numExtraFields = len(self.getExtraNames())
+ self.addScannableParts()
+ self.autoCompletePartialMoveToTargets = False
+ self.positionAtScanStart = None
+
+ def setAutoCompletePartialMoveToTargets(self, b):
+ self.autoCompletePartialMoveToTargets = b
+
+ def atScanStart(self):
+ self.positionAtScanStart = self.getPosition()
+
+ def atCommandFailure(self):
+ self.positionAtScanStart = None
+
+ def atScanEnd(self):
+ self.positionAtScanStart = None
+
+###
+
+ def __repr__(self):
+ pos = self.getPosition()
+ formattedValues = self.formatPositionFields(pos)
+ if len(tuple(self.getInputNames()) + tuple(self.getExtraNames())) > 1:
+ result = self.getName() + ': '
+ else:
+ result = ''
+
+ names = tuple(self.getInputNames()) + tuple(self.getExtraNames())
+ for name, val in zip(names, formattedValues):
+ result += ' ' + name + ': ' + val
+ return result
+###
+
+ def formatPositionFields(self, pos):
+ """Returns position as array of formatted strings"""
+ # Make sure pos is a tuple or list
+ if type(pos) not in (tuple, list):
+ pos = tuple([pos])
+
+ # Sanity check
+ if len(pos) != len(self.getOutputFormat()):
+ raise Exception(
+ "In scannable '%s':number of position fields differs from "
+ "number format strings specified" % self.getName())
+
+ result = []
+ for field, format in zip(pos, self.getOutputFormat()):
+ if field is None:
+ result.append('???')
+ else:
+ s = (format % field)
+## if width!=None:
+## s = s.ljust(width)
+ result.append(s)
+
+ return result
+
+###
+
+ def addScannableParts(self):
+ '''
+ Creates an array of MotionScannableParts each of which allows access to
+ the scannable's fields. See this class's documentation for more info.
+ '''
+ self.childrenDict = {}
+ # Add parts to access the input fields
+ for index in range(len(self.getInputNames())):
+ scannableName = self.getInputNames()[index]
+ self.childrenDict[scannableName] = self.MotionScannablePart(
+ scannableName, index, self, isInputField=1)
+
+ # Add parts to access the extra fields
+ for index in range(len(self.getExtraNames())):
+ scannableName = self.getExtraNames()[index]
+ self.childrenDict[scannableName] = self.MotionScannablePart(
+ scannableName, index + len(self.getInputNames()),
+ self, isInputField=0)
+
+ def asynchronousMoveTo(self, newpos):
+ if self.autoCompletePartialMoveToTargets:
+ newpos = self.completePosition(newpos)
+ ScannableBase.asynchronousMoveTo(self, newpos)
+
+ def completePosition(self, position):
+ '''
+ If position contains any null or None values, these are replaced with
+ the corresponding fields from the scannables current position and then
+ returned.'''
+ # Just return position if it does not need padding
+ if None not in position:
+ return position
+ if self.positionAtScanStart is not None:
+ basePosition = self.positionAtScanStart
+ else:
+ basePosition = self.getPosition()[:self.numInputFields]
+ for i in range(self.numInputFields):
+ if position[i] is None:
+ position[i] = basePosition[i]
+ return position
+
+ def __getattr__(self, name):
+ try:
+ return self.childrenDict[name]
+ except:
+ raise AttributeError("No child named:" + name)
+
+ def __getitem__(self, key):
+ '''Provides container like access from Jython'''
+ return self.childrenDict[key]
+
+ def getPart(self, name):
+ '''Returns the a compnent scannable'''
+ return self.childrenDict[name]
+
+ class MotionScannablePart(ScannableBase):
+ '''
+ A scannable to be placed in the parent's childrenDict that allows
+ access to the parent's individual fields.'''
+
+ def __init__(self, scannableName, index, parentScannable,
+ isInputField):
+ self.setName(scannableName)
+ if isInputField:
+ self.setInputNames([scannableName])
+ else:
+ self.setExtraNames([scannableName])
+ self.index = index
+ self.parentScannable = parentScannable
+ self.setOutputFormat(
+ [self.parentScannable.getOutputFormat()[index]])
+
+ def isBusy(self):
+ return self.parentScannable.isBusy()
+
+ def asynchronousMoveTo(self, new_position):
+ if self.parentScannable.isBusy():
+ raise Exception(
+ self.parentScannable.getName() + "." + self.getName() +
+ " cannot be moved because " +
+ self.parentScannable.getName() + " is already moving")
+
+ toMoveTo = [None] * len(self.parentScannable.getInputNames())
+ toMoveTo[self.index] = new_position
+ self.parentScannable.asynchronousMoveTo(toMoveTo)
+
+ def moveTo(self, new_position):
+ self.asynchronousMoveTo(new_position)
+ self.waitWhileBusy()
+
+ def getPosition(self):
+ return self.parentScannable.getPosition()[self.index]
+
+ def __str__(self):
+ return self.__repr__()
+
+ def __repr__(self):
+ # Get the name of this field
+ # (assume its an input field first and correct if wrong)
+ name = self.getInputNames()[0]
+
+ if name == 'value':
+ name = self.getExtraNames()[0]
+ parentName = self.parentScannable.getName()
+ return parentName + "." + name + " : " + str(self.getPosition())
+
+
+
+
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/__init__.py b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/base.py b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/base.py
new file mode 100755
index 0000000..dfa3182
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/base.py
@@ -0,0 +1,62 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+try:
+ from gda.device.scannable import PseudoDevice
+except ImportError:
+ from diffcalc.gdasupport.minigda.scannable import \
+ ScannableBase as PseudoDevice
+
+
+class ScannableGroup(PseudoDevice):
+
+ def __init__(self, name, motorList):
+
+ self.setName(name)
+ # Set input format
+ motorNames = []
+ for scn in motorList:
+ motorNames.append(scn.getName())
+ self.setInputNames(motorNames)
+ # Set output format
+ format = []
+ for motor in motorList:
+ format.append(motor.getOutputFormat()[0])
+ self.setOutputFormat(format)
+ self.__motors = motorList
+
+ def asynchronousMoveTo(self, position):
+ # if input has any Nones, then replace these with the current positions
+ if None in position:
+ position = list(position)
+ current = self.getPosition()
+ for idx, val in enumerate(position):
+ if val is None:
+ position[idx] = current[idx]
+
+ for scn, pos in zip(self.__motors, position):
+ scn.asynchronousMoveTo(pos)
+
+ def getPosition(self):
+ return [scn.getPosition() for scn in self.__motors]
+
+ def isBusy(self):
+ for scn in self.__motors:
+ if scn.isBusy():
+ return True
+ return False
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/diffractometer.py b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/diffractometer.py
new file mode 100755
index 0000000..1d1b993
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/diffractometer.py
@@ -0,0 +1,126 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+try:
+ from gda.device.scannable import ScannableMotionBase
+except ImportError:
+ from diffcalc.gdasupport.minigda.scannable import \
+ ScannableBase as ScannableMotionBase
+
+from diffcalc.util import getMessageFromException
+
+# TODO: Split into a base class when making other scannables
+
+
+class DiffractometerScannableGroup(ScannableMotionBase):
+ """"
+ Wraps up a scannableGroup of axis to tweak the way the resulting
+ object is displayed and to add a simulate move to method.
+
+ The scannable group should have the same geometry as that expected
+ by the diffractometer hardware geometry used in the diffraction
+ calculator.
+
+ The optional parameter slaveDriver can be used to provide a
+ slave_driver. This is useful for triggering a move of an incidental
+ axis whose position depends on that of the diffractometer, but whose
+ position need not be included in the DiffractometerScannableGroup
+ itself. This parameter is exposed as a field and can be set or
+ cleared to null at will without effecting the core calculation code.
+ """
+
+ def __init__(self, name, diffcalc_module, scannableGroup,
+ slave_driver=None, hint_generator=None):
+ # if motorList is None, will create a dummy __group
+ self.diffcalc_module = diffcalc_module
+ self.__group = scannableGroup
+ self.slave_driver = slave_driver
+ self.setName(name)
+ self.hint_generator = hint_generator
+
+ def getInputNames(self):
+ return self.__group.getInputNames()
+
+ def getExtraNames(self):
+ if self.slave_driver is None:
+ return []
+ else:
+ return self.slave_driver.getScannableNames()
+
+ def getOutputFormat(self):
+ if self.slave_driver is None:
+ slave_formats = []
+ else:
+ slave_formats = self.slave_driver.getScannableNames()
+ return list(self.__group.getOutputFormat()) + slave_formats
+
+ def asynchronousMoveTo(self, position):
+ self.__group.asynchronousMoveTo(position)
+ if self.slave_driver is not None:
+ self.slave_driver.triggerAsynchronousMove(position)
+
+ def getPosition(self):
+ if self.slave_driver is None:
+ slave_positions = []
+ else:
+ slave_positions = self.slave_driver.getPositions()
+ return list(self.__group.getPosition()) + list(slave_positions)
+
+ def isBusy(self):
+ if self.slave_driver is None:
+ return self.__group.isBusy()
+ else:
+ return self.__group.isBusy() or self.slave_driver.isBusy()
+
+ def waitWhileBusy(self):
+ self.__group.waitWhileBusy()
+ if self.slave_driver is not None:
+ self.slave_driver.waitWhileBusy()
+
+ def simulateMoveTo(self, pos):
+ if len(pos) != len(self.getInputNames()):
+ raise ValueError('Wrong number of inputs')
+ try:
+ (hkl, params) = self.diffcalc_module.angles_to_hkl(pos)
+ except Exception, e:
+ return "Error: %s" % getMessageFromException(e)
+ width = max(len(k) for k in params)
+
+ lines = ([' ' + 'hkl'.rjust(width) + ' : % 9.4f %.4f %.4f' %
+ (hkl[0], hkl[1], hkl[2])])
+ lines[-1] = lines[-1] + '\n'
+ fmt = ' %' + str(width) + 's : % 9.4f'
+ for k in sorted(params):
+ lines.append(fmt % (k, params[k]))
+ return '\n'.join(lines)
+
+ def __repr__(self):
+ position = self.getPosition()
+ names = list(self.getInputNames()) + list(self.getExtraNames())
+ if self.hint_generator is None:
+ hint_list = [''] * len(self.getInputNames())
+ else:
+ hint_list = self.hint_generator()
+
+ lines = [self.name + ':']
+ width = max(len(k) for k in names)
+ fmt = ' %' + str(width) + 's : % 9.4f %s'
+ for name, pos, hint in zip(names, position, hint_list):
+ lines.append(fmt % (name, pos, hint))
+ lines[len(self.getInputNames())] += '\n'
+ return '\n'.join(lines)
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/hkl.py b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/hkl.py
new file mode 100755
index 0000000..12376d2
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/hkl.py
@@ -0,0 +1,135 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+import platform
+
+DEBUG = False
+
+try:
+ from gda.device.scannable.scannablegroup import \
+ ScannableMotionWithScannableFieldsBase
+except ImportError:
+ from diffcalc.gdasupport.minigda.scannable import \
+ ScannableMotionWithScannableFieldsBase
+
+from diffcalc.util import getMessageFromException, DiffcalcException
+
+
+class _DynamicDocstringMetaclass(type):
+
+ def _get_doc(self):
+ return Hkl.dynamic_docstring
+
+ __doc__ = property(_get_doc) # @ReservedAssignment
+
+
+class Hkl(ScannableMotionWithScannableFieldsBase):
+
+ if platform.system() != 'Java':
+ __metaclass__ = _DynamicDocstringMetaclass # TODO: Removed to fix Jython
+
+ dynamic_docstring = 'Hkl Scannable'
+
+ def _get_doc(self):
+ return Hkl.dynamic_docstring
+
+ __doc__ = property(_get_doc) # @ReservedAssignment
+
+ def __init__(self, name, diffractometerObject, diffcalcObject,
+ virtualAnglesToReport=None):
+ self.diffhw = diffractometerObject
+ self._diffcalc = diffcalcObject
+ if type(virtualAnglesToReport) is str:
+ virtualAnglesToReport = (virtualAnglesToReport,)
+ self.vAngleNames = virtualAnglesToReport
+
+ self.setName(name)
+ self.setInputNames(['h', 'k', 'l'])
+ self.setOutputFormat(['%7.5f'] * 3)
+ if self.vAngleNames:
+ self.setExtraNames(self.vAngleNames)
+ self.setOutputFormat(['%7.5f'] * (3 + len(self.vAngleNames)))
+
+ self.completeInstantiation()
+ self.setAutoCompletePartialMoveToTargets(True)
+ self.dynamic_class_doc = 'Hkl Scannable xyz'
+
+ def rawAsynchronousMoveTo(self, hkl):
+ if len(hkl) != 3: raise ValueError('Hkl device expects three inputs')
+ try:
+ (pos, _) = self._diffcalc.hkl_to_angles(hkl[0], hkl[1], hkl[2])
+ except DiffcalcException, e:
+ if DEBUG:
+ raise
+ else:
+ raise DiffcalcException(e.message)
+ self.diffhw.asynchronousMoveTo(pos)
+
+ def rawGetPosition(self):
+ pos = self.diffhw.getPosition() # a tuple
+ (hkl , params) = self._diffcalc.angles_to_hkl(pos)
+ result = list(hkl)
+ if self.vAngleNames:
+ for vAngleName in self.vAngleNames:
+ result.append(params[vAngleName])
+ return result
+
+ def getFieldPosition(self, i):
+ return self.getPosition()[i]
+
+ def isBusy(self):
+ return self.diffhw.isBusy()
+
+ def waitWhileBusy(self):
+ return self.diffhw.waitWhileBusy()
+
+ def simulateMoveTo(self, hkl):
+ if type(hkl) not in (list, tuple):
+ raise ValueError('Hkl device expects three inputs')
+ if len(hkl) != 3:
+ raise ValueError('Hkl device expects three inputs')
+ (pos, params) = self._diffcalc.hkl_to_angles(hkl[0], hkl[1], hkl[2])
+
+ width = max(len(k) for k in (params.keys() + list(self.diffhw.getInputNames())))
+ fmt = ' %' + str(width) + 's : % 9.4f'
+
+ lines = [self.diffhw.getName() + ' would move to:']
+ for idx, name in enumerate(self.diffhw.getInputNames()):
+ lines.append(fmt % (name, pos[idx]))
+ lines[-1] = lines[-1] + '\n'
+ for k in sorted(params):
+ lines.append(fmt % (k, params[k]))
+ return '\n'.join(lines)
+
+ def __str__(self):
+ return self.__repr__()
+
+ def __repr__(self):
+ lines = ['hkl:']
+ pos = self.diffhw.getPosition()
+ try:
+ (hkl, params) = self._diffcalc.angles_to_hkl(pos)
+ except Exception, e:
+ return "" % getMessageFromException(e)
+
+ width = max(len(k) for k in params)
+ lines.append(' ' + 'hkl'.rjust(width) + ' : %9.4f %.4f %.4f' % (hkl[0], hkl[1], hkl[2]))
+ lines[-1] = lines[-1] + '\n'
+ fmt = ' %' + str(width) + 's : % 9.4f'
+ for k in sorted(params):
+ lines.append(fmt % (k, params[k]))
+ return '\n'.join(lines)
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/mock.py b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/mock.py
new file mode 100755
index 0000000..13871da
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/mock.py
@@ -0,0 +1,47 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+try:
+ from gda.device.scannable import ScannableMotionBase
+except ImportError:
+ from diffcalc.gdasupport.minigda.scannable import \
+ ScannableBase as ScannableMotionBase
+
+
+class MockMotor(ScannableMotionBase):
+
+ def __init__(self, name='mock'):
+ self.pos = 0.0
+ self._busy = False
+ self.name = name
+
+ def asynchronousMoveTo(self, pos):
+ self._busy = True
+ self.pos = float(pos)
+
+ def getPosition(self):
+ return self.pos
+
+ def isBusy(self):
+ return self._busy
+
+ def makeNotBusy(self):
+ self._busy = False
+
+ def getOutputFormat(self):
+ return ['%f']
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/parameter.py b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/parameter.py
new file mode 100755
index 0000000..a08e95c
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/parameter.py
@@ -0,0 +1,45 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+try:
+ from gda.device.scannable import ScannableMotionBase
+except ImportError:
+ from diffcalc.gdasupport.minigda.scannable import \
+ ScannableBase as ScannableMotionBase
+
+
+class DiffractionCalculatorParameter(ScannableMotionBase):
+
+ def __init__(self, name, parameterName, parameter_manager):
+
+ self.parameter_manager = parameter_manager
+ self.parameterName = parameterName
+
+ self.setName(name)
+ self.setInputNames([parameterName])
+ self.setOutputFormat(['%5.5f'])
+ self.setLevel(3)
+
+ def asynchronousMoveTo(self, value):
+ self.parameter_manager.set_constraint(self.parameterName, value)
+
+ def getPosition(self):
+ return self.parameter_manager.get_constraint(self.parameterName)
+
+ def isBusy(self):
+ return False
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/sim.py b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/sim.py
new file mode 100755
index 0000000..44b2108
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/sim.py
@@ -0,0 +1,21 @@
+'''
+Created on 7 May 2016
+
+@author: walton
+'''
+from diffcalc.util import allnum
+
+def sim(scn, hkl):
+ """sim hkl scn -- simulates moving scannable (not all)
+ """
+ if not isinstance(hkl, (tuple, list)):
+ raise TypeError
+
+ if not allnum(hkl):
+ raise TypeError()
+
+ try:
+ print scn.simulateMoveTo(hkl)
+ except AttributeError:
+ raise TypeError(
+ "The first argument does not support simulated moves")
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/simulation.py b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/simulation.py
new file mode 100755
index 0000000..0255a5f
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/simulation.py
@@ -0,0 +1,139 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+import time
+from math import sqrt, pi, exp
+
+try:
+ from gda.device.scannable import PseudoDevice
+except ImportError:
+ from diffcalc.gdasupport.minigda.scannable import \
+ ScannableBase as PseudoDevice
+
+from diffcalc.ub.crystal import CrystalUnderTest
+from diffcalc.hkl.you.calc import youAnglesToHkl
+from diffcalc.hkl.vlieg.calc import vliegAnglesToHkl
+from diffcalc.hkl.you.geometry import calcCHI, calcPHI
+
+TORAD = pi / 180
+TODEG = 180 / pi
+
+
+class Equation(object):
+
+ def __call__(self, dh, dk, dl):
+ raise Exception('Abstract')
+
+ def __str__(self):
+ "Abstract equation"
+
+
+class Gaussian(Equation):
+
+ def __init__(self, variance):
+ self.variance = float(variance)
+
+ def __call__(self, dh, dk, dl):
+ dr_squared = dh * dh + dk * dk + dl * dl
+ return (1 / sqrt(2 * pi * self.variance) *
+ exp(-dr_squared / (2 * self.variance)))
+
+
+class SimulatedCrystalCounter(PseudoDevice):
+
+ def __init__(self, name, diffractometerScannable, geometryPlugin,
+ wavelengthScannable, equation=Gaussian(.01), engine='you'):
+ self.setName(name)
+ self.setInputNames([name + '_count'])
+ self.setOutputFormat(['%7.5f'])
+ self.exposureTime = 1
+ self.pause = True
+ self.diffractometerScannable = diffractometerScannable
+ self.geometry = geometryPlugin
+ self.wavelengthScannable = wavelengthScannable
+ self.equation = equation
+ self.engine = engine
+
+ self.cut = None
+ self.UB = None
+ self.chiMissmount = 0.
+ self.phiMissmount = 0.
+ self.setCrystal('cubic', 1, 1, 1, 90, 90, 90)
+
+ def setCrystal(self, name, a, b, c, alpha, beta, gamma):
+ self.cut = CrystalUnderTest(name, a, b, c, alpha, beta, gamma)
+ self.calcUB()
+
+ def setChiMissmount(self, chi):
+ self.chiMissmount = chi
+ self.calcUB()
+
+ def setPhiMissmount(self, phi):
+ self.phiMissmount = phi
+ self.calcUB()
+
+ def calcUB(self):
+ CHI = calcCHI(self.chiMissmount * TORAD)
+ PHI = calcPHI(self.phiMissmount * TORAD)
+ self.UB = CHI * PHI * self.cut.B
+
+ def asynchronousMoveTo(self, exposureTime):
+ self.exposureTime = exposureTime
+ if self.pause:
+ time.sleep(exposureTime) # Should not technically block!
+
+ def getPosition(self):
+ h, k, l = self.getHkl()
+ dh, dk, dl = h - round(h), k - round(k), l - round(l)
+ count = self.equation(dh, dk, dl)
+ #return self.exposureTime, count*self.exposureTime
+ return count * self.exposureTime
+
+ def getHkl(self):
+ pos = self.geometry.physical_angles_to_internal_position(
+ self.diffractometerScannable.getPosition())
+ pos.changeToRadians()
+ wavelength = self.wavelengthScannable.getPosition()
+ if self.engine.lower() == 'vlieg':
+ return vliegAnglesToHkl(pos, wavelength, self.UB)
+ elif self.engine.lower() == 'you':
+ return youAnglesToHkl(pos, wavelength, self.UB)
+ else:
+ raise ValueError(self.engine)
+
+ def isBusy(self):
+ return False
+
+ def __str__(self):
+ return self.__repr__()
+
+ def __repr__(self):
+ s = 'simulated crystal detector: %s\n' % self.getName()
+ h, k, l = self.getHkl()
+ s += ' h : %f\n' % h
+ s += ' k : %f\n' % k
+ s += ' l : %f\n' % l
+ s += self.cut.__str__() + '\n'
+ s += "chi orientation: %s\n" % self.chiMissmount
+ s += "phi orientation: %s\n" % self.phiMissmount
+ ub = self.UB.tolist()
+ s += "UB:\n"
+ s += " % 18.13f% 18.13f% 18.12f\n" % (ub[0][0], ub[0][1], ub[0][2])
+ s += " % 18.13f% 18.13f% 18.12f\n" % (ub[1][0], ub[1][1], ub[1][2])
+ s += " % 18.13f% 18.13f% 18.12f\n" % (ub[2][0], ub[2][1], ub[2][2])
+ return s
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/slave_driver.py b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/slave_driver.py
new file mode 100755
index 0000000..cd5ce24
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/slave_driver.py
@@ -0,0 +1,109 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from math import pi, tan, sin, atan, cos, atan2
+
+TORAD = pi / 180
+TODEG = 180 / pi
+
+
+class SlaveScannableDriver(object):
+
+ def __init__(self, scannables):
+ self.scannables = scannables
+
+ def isBusy(self):
+ for scn in self.scannables:
+ if scn.isBusy():
+ return True
+ return False
+
+ def waitWhileBusy(self):
+ for scn in self.scannables:
+ scn.waitWhileBusy()
+
+ def triggerAsynchronousMove(self, triggerPos):
+ nu = self.slaveFromTriggerPos(triggerPos)
+ for scn in self.scannables:
+ scn.asynchronousMoveTo(nu)
+
+ def getPosition(self):
+ return self.scannables[0].getPosition()
+
+ def slaveFromTriggerPos(self, triggerPos):
+ raise Exception("Abstract")
+
+ def getScannableNames(self):
+ return [scn.name for scn in self.scannables]
+
+ def getOutputFormat(self):
+ return [list(scn.outputFormat)[0] for scn in self.scannables]
+
+ def getPositions(self):
+ return [float(scn.getPosition()) for scn in self.scannables]
+
+
+"""
+Based on: Elias Vlieg, "A (2+3)-Type Surface Diffractometer: Mergence of the
+z-axis and (2+2)-Type Geometries", J. Appl. Cryst. (1998). 31. 198-203
+"""
+
+
+class NuDriverForSixCirclePlugin(SlaveScannableDriver):
+
+ def slaveFromTriggerPos(self, triggerPos):
+
+ alpha, delta, gamma, _, _, _ = triggerPos
+ alpha = alpha * TORAD
+ delta = delta * TORAD
+ gamma = gamma * TORAD
+
+ ### Equation16 RHS ###
+ rhs = -1 * tan(gamma - alpha) * sin(delta)
+ nu = atan(rhs) # -pi/2 <= nu <= pi/2
+ return nu * TODEG
+
+
+class NuDriverForWillmottHorizontalGeometry(SlaveScannableDriver):
+
+ """
+ Based on: Phillip Willmott, "Angle calculations for a (2+3)-type
+ diffractometer: focus on area detectors", J. Appl. Cryst. (2011). 44.
+ 73-83
+ """
+
+ def __init__(self, scannables, area_detector=False):
+ SlaveScannableDriver.__init__(self, scannables)
+ self.area_detector = area_detector
+
+ def slaveFromTriggerPos(self, triggerPos):
+
+ delta, gamma, omegah, _ = triggerPos
+ delta *= TORAD
+ gamma *= TORAD
+ omegah *= TORAD
+ if self.area_detector:
+ nu = atan2(sin(delta - omegah), tan(gamma)) # (66)
+ else:
+ top = -sin(gamma) * sin(omegah)
+ bot = (sin(omegah) * cos(gamma) * sin(delta) +
+ cos(omegah) * cos(delta))
+ nu = atan2(top, bot) # (61)
+
+ print 'nu:', nu * TODEG
+ return nu * TODEG
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/vrmlanimator.py b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/vrmlanimator.py
new file mode 100755
index 0000000..9f40441
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/vrmlanimator.py
@@ -0,0 +1,184 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+import time
+import threading
+import socket
+PORT = 4567
+
+from gda.device.scannable import ScannableMotionWithScannableFieldsBaseTest
+
+#import scannable.vrmlModelDriver
+#reload(scannable.vrmlModelDriver);from scannable.vrmlModelDriver import \
+# VrmlModelDriver, LinearProfile, MoveThread
+#fc=VrmlModelDriver(
+# 'fc',['alpha','delta','omega', 'chi','phi'], speed=30, host='diamrl5104')
+#alpha = fc.alpha
+#delta = fc.delta
+#omega = fc.omega
+#chi = fc.chi
+#phi = fc.phi
+
+
+def connect_to_socket(host, port):
+ print "Connecting to %s on port %d" % (host, port)
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.connect((host, port))
+ print "Connected"
+ socketfile = sock.makefile('rw', 0)
+ return socketfile
+
+
+class LinearProfile(object):
+
+ def __init__(self, v, t_accel, startList, endList):
+ assert len(startList) == len(endList)
+ self.v = float(v)
+ self.start = startList
+ self.end = endList
+ self.t_accel = t_accel
+
+ distances = [e - s for e, s in zip(self.end, self.start)]
+ max_distance = max([abs(d) for d in distances])
+ if max_distance == 0:
+ self.delta_time = 0
+ else:
+ self.delta_time = abs(max_distance / self.v)
+ self.speeds = [d / self.delta_time for d in distances]
+ self.start_time = time.time()
+
+ def getPosition(self):
+ if self.start_time is None:
+ return self.start
+ if not self.isMoving():
+ return self.end
+ t = abs(float(time.time() - self.start_time))
+ if t > self.delta_time:
+ # we are in the deceleration phase (i.e paused for now)
+ return self.end
+ return [s + v * t for s, v in zip(self.start, self.speeds)]
+
+ def isMoving(self):
+ return time.time() < self.start_time + self.delta_time + self.t_accel
+
+
+class MoveThread(threading.Thread):
+
+ def __init__(self, profile, socketfile, axisNames):
+ threading.Thread.__init__(self)
+ self.profile = profile
+ self.socketfile = socketfile
+ self.axisNames = axisNames
+
+ def run(self):
+ while self.profile.isMoving():
+ self.update()
+ time.sleep(.1)
+ self.update()
+
+ def update(self):
+ pos = self.profile.getPosition()
+ d = dict(zip(map(str, self.axisNames), pos))
+ if self.socketfile:
+ self.socketfile.write(repr(d) + '\n')
+
+
+class VrmlModelDriver(ScannableMotionWithScannableFieldsBaseTest):
+
+ def __init__(self, name, axes_names, host=None, speed=60, t_accel=.1,
+ format='%.3f'):
+ self.name = name
+ self.inputNames = list(axes_names)
+ self.extraNames = []
+ self.outputFormat = [format] * len(self.inputNames)
+ self.completeInstantiation()
+ self.__last_target = [0.] * len(self.inputNames)
+ self.verbose = False
+ self.move_thread = None
+ self.speed = speed
+ self.host = host
+ self.t_accel = t_accel
+ self.socketfile = None
+ if self.host:
+ try:
+ self.connect()
+ except socket.error:
+ print "Failed to connect to %s:%r" % (self.host, PORT)
+ print "Connect with: %s.connect()" % self.name
+
+ def connect(self):
+ self.socketfile = connect_to_socket(self.host, PORT)
+ self.rawAsynchronousMoveTo(self.__last_target)
+
+ def isBusy(self):
+ if self.move_thread is None:
+ return False
+ return self.move_thread.profile.isMoving()
+
+ def rawGetPosition(self):
+ if self.move_thread is None:
+ return self.__last_target
+ else:
+ return self.move_thread.profile.getPosition()
+
+ def rawAsynchronousMoveTo(self, targetList):
+ if self.isBusy():
+ raise Exception(self.name + ' is already moving')
+ if self.verbose:
+ print self.name + ".rawAsynchronousMoveTo(%r)" % targetList
+
+ for i, target in enumerate(targetList):
+ if target is None:
+ targetList[i] = self.__last_target[i]
+ profile = LinearProfile(
+ self.speed, self.t_accel, self.__last_target, targetList)
+ self.move_thread = MoveThread(
+ profile, self.socketfile, self.inputNames)
+ self.move_thread.start()
+ self.__last_target = targetList
+
+ def getFieldPosition(self, index):
+ return self.getPosition()[index]
+
+ def __del__(self):
+ self.socketfile.close()
+
+#class TrapezoidProfile(object):
+#
+# def __init__(self, t_accel, v_max, delta_x):
+# self.t_a = t_accel
+# self.v_m = v_max
+# self.delta_x = delta_x
+#
+# self.t_c = (self.X - self.v_m*self.t_a) / self.v_m
+#
+# def x(self, t):
+# if self.t_c <=0:
+# return self.__xshort(t)
+# else:
+# return self.__xlong(t)
+#
+# def __xshort(self, t):
+# delta_t = 2 * sqrt(self.delta_x*self.t_a/self.v_m)
+# if t <= .5*delta_t:
+# return (.5*self.v_m/self.t_a) * t**2
+# else:
+# v_peak = (self.v_m/self.t_a) * .5*delta_t
+# return (t-.5*delta_t)*v_peak - (t-.5*delta_t)**2 ####HERE, bugged
+# self.delta_x/2
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/wavelength.py b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/wavelength.py
new file mode 100755
index 0000000..52fd925
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/wavelength.py
@@ -0,0 +1,50 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+try:
+ from gdascripts.pd.dummy_pds import DummyPD
+except ImportError:
+ from diffcalc.gdasupport.minigda.scannable import DummyPD
+
+
+class Wavelength(DummyPD):
+
+ def __init__(self, name, energyScannable,
+ energyScannableMultiplierToGetKeV=1):
+ self.energyScannable = energyScannable
+ self.energyScannableMultiplierToGetKeV = \
+ energyScannableMultiplierToGetKeV
+
+ DummyPD.__init__(self, name)
+
+ def asynchronousMoveTo(self, pos):
+ self.energyScannable.asynchronousMoveTo(
+ (12.39842 / pos) / self.energyScannableMultiplierToGetKeV)
+
+ def getPosition(self):
+ energy = self.energyScannable.getPosition()
+ if energy == 0:
+ raise Exception(
+ "The energy is 0, so no wavelength could be calculated.run_All()")
+ return 12.39842 / (energy * self.energyScannableMultiplierToGetKeV)
+
+ def isBusy(self):
+ return self.energyScannable.isBusy()
+
+ def waitWhileBusy(self):
+ return self.energyScannable.waitWhileBusy()
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/you.py b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/you.py
new file mode 100755
index 0000000..609a344
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/gdasupport/you.py
@@ -0,0 +1,113 @@
+from diffcalc.gdasupport.scannable.diffractometer import DiffractometerScannableGroup
+from diffcalc.gdasupport.scannable.hkl import Hkl
+from diffcalc.gdasupport.scannable.simulation import SimulatedCrystalCounter
+from diffcalc.gdasupport.scannable.wavelength import Wavelength
+from diffcalc.gdasupport.scannable.parameter import DiffractionCalculatorParameter
+
+
+from diffcalc.dc import dcyou as _dc
+from diffcalc.dc.help import format_command_help
+reload(_dc)
+from diffcalc.dc.dcyou import * # @UnusedWildImport
+from diffcalc import settings
+
+try:
+ import gda # @UnusedImport @UnresolvedImport
+ GDA = True
+except:
+ GDA = False
+
+if not GDA:
+ from diffcalc.gdasupport.minigda import command
+ _pos = command.Pos()
+ _scan = command.Scan(command.ScanDataPrinter())
+
+ def pos(*args):
+ """
+ pos show position of all Scannables
+ pos scn show position of scn
+ pos scn targetmove scn to target (a number)
+ """
+ return _pos(*args)
+
+ def scan(*args):
+ """
+ scan scn start stop step {scn {target}} {det t}
+ """
+ return _scan(*args)
+
+
+from diffcalc.gdasupport.scannable.sim import sim # @UnusedImport
+
+_scn_group = settings.axes_scannable_group
+_diff_scn_name = settings.geometry.name # @UndefinedVariable
+_energy_scannable = settings.energy_scannable
+
+
+# Create diffractometer scannable
+_diff_scn = DiffractometerScannableGroup(_diff_scn_name, _dc, _scn_group)
+globals()[_diff_scn_name] = _diff_scn
+
+# Create hkl scannables
+hkl = Hkl('hkl', _scn_group, _dc)
+h = hkl.h
+k = hkl.k
+l = hkl.l
+
+Hkl.dynamic_docstring = format_command_help(hkl_commands_for_help) # must be on the class
+ub.__doc__ = format_command_help(ub_commands_for_help)
+
+_virtual_angles = ('theta', 'qaz', 'alpha', 'naz', 'tau', 'psi', 'beta')
+hklverbose = Hkl('hklverbose', _scn_group, _dc, _virtual_angles)
+
+
+# Create wavelength scannable
+wl = Wavelength(
+ 'wl', _energy_scannable, settings.energy_scannable_multiplier_to_get_KeV)
+if not GDA:
+ wl.asynchronousMoveTo(1) # Angstrom
+_energy_scannable.level = 3
+wl.level = 3
+
+
+# Create simulated counter timer
+ct = SimulatedCrystalCounter('ct', _scn_group, settings.geometry, wl)
+ct.level = 10
+
+
+# Create constraint scannables
+def _create_constraint_scannable(con_name, scn_name=None):
+ if not scn_name:
+ scn_name = con_name
+ return DiffractionCalculatorParameter(
+ scn_name, con_name, _dc.constraint_manager)
+
+# Detector constraints
+def isconstrainable(name):
+ return not constraint_manager.is_constraint_fixed(name)
+
+if isconstrainable('delta'): delta_con = _create_constraint_scannable('delta', 'delta_con')
+if isconstrainable('gam'): gam_con = _create_constraint_scannable('gam', 'gam_con')
+if isconstrainable('qaz'): qaz = _create_constraint_scannable('qaz')
+if isconstrainable('naz'): naz = _create_constraint_scannable('naz')
+
+# Reference constraints
+alpha = _create_constraint_scannable('alpha')
+beta = _create_constraint_scannable('beta')
+psi = _create_constraint_scannable('psi')
+a_eq_b = 'a_eq_b'
+
+# Sample constraints
+if isconstrainable('mu'): mu_con = _create_constraint_scannable('mu', 'mu_con')
+if isconstrainable('eta'): eta_con = _create_constraint_scannable('eta', 'eta_con')
+if isconstrainable('chi'): chi_con = _create_constraint_scannable('chi', 'chi_con')
+if isconstrainable('phi'): phi_con = _create_constraint_scannable('phi', 'phi_con')
+if isconstrainable('mu') and isconstrainable('gam'): mu_is_gam = 'mu_is_gam'
+
+
+# Cleanup to allow "from gdasupport.you import *"
+del DiffractometerScannableGroup, Hkl, SimulatedCrystalCounter
+del Wavelength, DiffractionCalculatorParameter
+
+# Cleanup other cruft
+del format_command_help
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hardware.py b/script/__Lib/diffcalc-2.1/diffcalc/hardware.py
new file mode 100755
index 0000000..274feaf
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/hardware.py
@@ -0,0 +1,382 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from __future__ import absolute_import
+
+from diffcalc.util import DiffcalcException
+from diffcalc import settings
+
+SMALL = 1e-8
+
+from diffcalc.util import command
+
+__all__ = ['hardware', 'setcut', 'setmin', 'setmax']
+
+
+def getNameFromScannableOrString(o):
+ try: # it may be a scannable
+ return o.getName()
+ except AttributeError:
+ return str(o)
+
+
+
+@command
+def hardware():
+ """hardware -- show diffcalc limits and cuts"""
+ print settings.hardware.repr_sector_limits_and_cuts() # @UndefinedVariable
+
+@command
+def setcut(scannable_or_string=None, val=None):
+ """setcut {name {val}} -- sets cut angle
+ """
+ if scannable_or_string is None and val is None:
+ print settings.hardware.repr_sector_limits_and_cuts() # @UndefinedVariable
+ else:
+ name = getNameFromScannableOrString(scannable_or_string)
+ if val is None:
+ print '%s: %f' % (name, settings.hardware.get_cuts()[name]) # @UndefinedVariable
+ else:
+ oldcut = settings.hardware.get_cuts()[name] # @UndefinedVariable
+ settings.hardware.set_cut(name, float(val)) # @UndefinedVariable
+ newcut = settings.hardware.get_cuts()[name] # @UndefinedVariable
+
+@command
+def setmin(name=None, val=None):
+ """setmin {axis {val}} -- set lower limits used by auto sector code (None to clear)""" #@IgnorePep8
+ _setMinOrMax(name, val, settings.hardware.set_lower_limit) # @UndefinedVariable
+
+@command
+def setmax(name=None, val=None):
+ """setmax {name {val}} -- sets upper limits used by auto sector code (None to clear)""" #@IgnorePep8
+ _setMinOrMax(name, val, settings.hardware.set_upper_limit) # @UndefinedVariable
+
+@command
+def setrange(name=None, lower=None, upper=None):
+ """setrange {axis {min} {max}} -- set lower and upper limits used by auto sector code (None to clear)""" #@IgnorePep8
+ _setMinOrMax(name, lower, settings.hardware.set_lower_limit) # @UndefinedVariable
+ _setMinOrMax(name, upper, settings.hardware.set_upper_limit) # @UndefinedVariable
+
+def _setMinOrMax(name, val, setMethod):
+ if name is None:
+ print settings.hardware.repr_sector_limits_and_cuts() # @UndefinedVariable
+ else:
+ name = getNameFromScannableOrString(name)
+ if val is None:
+ print settings.hardware.repr_sector_limits_and_cuts(name) # @UndefinedVariable
+ else:
+ setMethod(name, float(val))
+
+
+commands_for_help = ['Hardware',
+ hardware,
+ setcut,
+ setmin,
+ setmax]
+
+
+class HardwareAdapter(object):
+
+ def __init__(self, diffractometerAngleNames, defaultCuts={},
+ energyScannableMultiplierToGetKeV=1):
+
+ self._diffractometerAngleNames = diffractometerAngleNames
+ self._upperLimitDict = {}
+ self._lowerLimitDict = {}
+ self._cut_angles = {}
+ self._configure_cuts(defaultCuts)
+ self.energyScannableMultiplierToGetKeV = \
+ energyScannableMultiplierToGetKeV
+ self._name = 'base'
+
+ @property
+ def name(self):
+ return self._name
+
+ def get_axes_names(self):
+ return tuple(self._diffractometerAngleNames)
+
+ def get_position(self):
+ """pos = get_position() -- returns the current physical diffractometer
+ position as a diffcalc.util object in degrees
+ """
+ raise NotImplementedError()
+
+ def get_wavelength(self):
+ """wavelength = get_wavelength() -- returns wavelength in Angstroms
+ """
+ return 12.39842 / self.get_energy()
+
+ def get_energy(self):
+ """energy = get_energy() -- returns energy in kEv """
+ raise NotImplementedError()
+
+ def __str__(self):
+ s = self.name + ":\n"
+ s += " energy : " + str(self.get_energy()) + " keV\n"
+ s += " wavelength : " + str(self.get_wavelength()) + " Angstrom\n"
+ names = self._diffractometerAngleNames
+ for name, pos in zip(names, self.get_position()):
+ s += " %s : %r deg\n" % (name, pos)
+ return s
+
+ def __repr__(self):
+ return self.__str__()
+
+ def get_position_by_name(self, angleName):
+ names = list(self._diffractometerAngleNames)
+ return self.get_position()[names.index(angleName)]
+
+### Limits ###
+
+ def get_lower_limit(self, name):
+ '''returns lower limits by axis name. Limit may be None if not set
+ '''
+ if name not in self._diffractometerAngleNames:
+ raise ValueError("No angle called %s. Try one of: %s" %
+ (name, self._diffractometerAngleNames))
+ return self._lowerLimitDict.get(name)
+
+ def get_upper_limit(self, name):
+ '''returns upper limit by axis name. Limit may be None if not set
+ '''
+ if name not in self._diffractometerAngleNames:
+ raise ValueError("No angle called %s. Try one of: %s" %
+ name, self._diffractometerAngleNames)
+ return self._upperLimitDict.get(name)
+
+ def set_lower_limit(self, name, value):
+ """value may be None to remove limit"""
+ if name not in self._diffractometerAngleNames:
+ raise ValueError(
+ "Cannot set lower Diffcalc limit: No angle called %s. Try one "
+ "of: %s" % (name, self._diffractometerAngleNames))
+ if value is None:
+ try:
+ del self._lowerLimitDict[name]
+ except KeyError:
+ print ("WARNING: There was no lower Diffcalc limit %s set to "
+ "clear" % name)
+ else:
+ self._lowerLimitDict[name] = value
+
+ def set_upper_limit(self, name, value):
+ """value may be None to remove limit"""
+ if name not in self._diffractometerAngleNames:
+ raise ValueError(
+ "Cannot set upper Diffcalc limit: No angle called %s. Try one "
+ "of: %s" % (name, self._diffractometerAngleNames))
+ if value is None:
+ try:
+ del self._upperLimitDict[name]
+ except KeyError:
+ print ("WARNING: There was no upper Diffcalc limit %s set to "
+ "clear" % name)
+ else:
+ self._upperLimitDict[name] = value
+
+ def is_position_within_limits(self, positionArray):
+ """
+ where position array is in degrees and cut to be between -180 and 180
+ """
+ names = self._diffractometerAngleNames
+ for axis_name, value in zip(names, positionArray):
+ if not self.is_axis_value_within_limits(axis_name, value):
+ return False
+ return True
+
+ def is_axis_value_within_limits(self, axis_name, value):
+ if axis_name in self._upperLimitDict:
+ if value > self._upperLimitDict[axis_name]:
+ return False
+ if axis_name in self._lowerLimitDict:
+ if value < self._lowerLimitDict[axis_name]:
+ return False
+ return True
+
+ def repr_sector_limits_and_cuts(self, name=None):
+ if name is None:
+ s = ''
+ for name in self.get_axes_names():
+ s += self.repr_sector_limits_and_cuts(name) + '\n'
+ s += "Note: When auto sector/transforms are used,\n "
+ s += " cuts are applied before checking limits."
+ return s
+ # limits:
+ low = self.get_lower_limit(name)
+ high = self.get_upper_limit(name)
+ s = ' '
+ if low is not None:
+ s += "% 6.1f <= " % low
+ else:
+ s += ' ' * 10
+ s += '%5s' % name
+ if high is not None:
+ s += " <= % 6.1f" % high
+ else:
+ s += ' ' * 10
+ # cuts:
+ try:
+ if self.get_cuts()[name] is not None:
+ s += " (cut: % 6.1f)" % self.get_cuts()[name]
+ except KeyError:
+ pass
+
+ return s
+
+### Cutting Stuff ###
+
+ def _configure_cuts(self, defaultCutsDict):
+ # 1. Set default cut angles
+ self._cut_angles = dict.fromkeys(self._diffractometerAngleNames, -180.)
+ if 'phi' in self._cut_angles:
+ self._cut_angles['phi'] = 0.
+ # 2. Overide with user-specified cuts
+ for name, val in defaultCutsDict.iteritems():
+ self.set_cut(name, val)
+
+ def set_cut(self, name, value):
+ if name in self._cut_angles:
+ self._cut_angles[name] = value
+ else:
+ raise KeyError("Diffractometer has no angle %s. Try: %s." %
+ (name, self._diffractometerAngleNames))
+
+ def get_cuts(self):
+ return self._cut_angles
+
+ def cut_angles(self, positionArray):
+ '''Assumes each angle in positionArray is between -360 and 360
+ '''
+ cutArray = []
+ names = self._diffractometerAngleNames
+ for axis_name, value in zip(names, positionArray):
+ cutArray.append(self.cut_angle(axis_name, value))
+ return tuple(cutArray)
+
+ def cut_angle(self, axis_name, value):
+ cut_angle = self._cut_angles[axis_name]
+ if cut_angle is None:
+ return value
+ return cut_angle_at(cut_angle, value)
+
+
+def cut_angle_at(cut_angle, value):
+ if (cut_angle == 0 and (abs(value - 360) < SMALL) or
+ (abs(value + 360) < SMALL) or
+ (abs(value) < SMALL)):
+ value = 0.
+ if value < (cut_angle - SMALL):
+ return value + 360.
+ elif value >= cut_angle + 360. + SMALL:
+ return value - 360.
+ else:
+ return value
+
+
+class DummyHardwareAdapter(HardwareAdapter):
+
+ def __init__(self, diffractometerAngleNames):
+ super(self.__class__, self).__init__(diffractometerAngleNames)
+# HardwareAdapter.__init__(self, diffractometerAngleNames)
+
+ self._position = [0.] * len(diffractometerAngleNames)
+ self._wavelength = 1.
+ self.energyScannableMultiplierToGetKeV = 1
+ self._name = "Dummy"
+
+# Required methods
+
+ def get_position(self):
+ """
+ pos = getDiffractometerPosition() -- returns the current physical
+ diffractometer position as a list in degrees
+ """
+ return self._position
+
+ def _set_position(self, pos):
+ assert len(pos) == len(self.get_axes_names()), \
+ "Wrong length of input list"
+ self._position = pos
+
+ position = property(get_position, _set_position)
+
+ def get_energy(self):
+ """energy = get_energy() -- returns energy in kEv """
+ if self._wavelength is None:
+ raise DiffcalcException(
+ "Energy or wavelength have not been set")
+ return (12.39842 /
+ (self._wavelength * self.energyScannableMultiplierToGetKeV))
+
+ def _set_energy(self, energy):
+ self._wavelength = 12.39842 / energy
+
+ energy = property(get_energy, _set_energy)
+
+ def get_wavelength(self):
+ """wavelength = get_wavelength() -- returns wavelength in Angstroms"""
+ if self._wavelength is None:
+ raise DiffcalcException(
+ "Energy or wavelength have not been set")
+ return self._wavelength
+
+ def _set_wavelength(self, wavelength):
+ self._wavelength = wavelength
+
+ wavelength = property(get_wavelength, _set_wavelength)
+
+
+class ScannableHardwareAdapter(HardwareAdapter):
+
+ def __init__(self, diffractometerScannable, energyScannable,
+ energyScannableMultiplierToGetKeV=1):
+ input_names = diffractometerScannable.getInputNames()
+ super(self.__class__, self).__init__(input_names)
+# HardwareAdapter.__init__(self, input_names)
+ self.diffhw = diffractometerScannable
+ self.energyhw = energyScannable
+ self.energyScannableMultiplierToGetKeV = \
+ energyScannableMultiplierToGetKeV
+ self._name = "ScannableHarwdareMonitor"
+
+# Required methods
+
+ def get_position(self):
+ """
+ pos = getDiffractometerPosition() -- returns the current physical
+ diffractometer position as a list in degrees
+ """
+ return self.diffhw.getPosition()
+
+ def get_energy(self):
+ """energy = get_energy() -- returns energy in kEv (NOT eV!) """
+ multiplier = self.energyScannableMultiplierToGetKeV
+ energy = self.energyhw.getPosition() * multiplier
+ if energy is None:
+ raise DiffcalcException("Energy has not been set")
+ return energy
+
+ def get_wavelength(self):
+ """wavelength = get_wavelength() -- returns wavelength in Angstroms"""
+ energy = self.get_energy()
+ return 12.39842 / energy
+
+ @property
+ def name(self):
+ return self.diffhw.getName()
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/__init__.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/calcbase.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/calcbase.py
new file mode 100755
index 0000000..c4db5f3
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/hkl/calcbase.py
@@ -0,0 +1,155 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from math import pi
+
+from diffcalc.util import DiffcalcException, differ
+
+TORAD = pi / 180
+TODEG = 180 / pi
+
+
+class HklCalculatorBase(object):
+
+ def __init__(self, ubcalc, geometry, hardware,
+ raiseExceptionsIfAnglesDoNotMapBackToHkl=False):
+
+ self._ubcalc = ubcalc # to get the UBMatrix, tau and sigma
+ self._geometry = geometry # to access information about the
+ # diffractometer geometry and mode_selector
+ self._hardware = hardware # Used for tracking parameters only
+ self.raiseExceptionsIfAnglesDoNotMapBackToHkl = \
+ raiseExceptionsIfAnglesDoNotMapBackToHkl
+
+ def anglesToHkl(self, pos, wavelength):
+ """
+ Return hkl tuple and dictionary of all virtual angles in degrees from
+ Position in degrees and wavelength in Angstroms.
+ """
+
+ h, k, l = self._anglesToHkl(pos.inRadians(), wavelength)
+ paramDict = self.anglesToVirtualAngles(pos, wavelength)
+ return ((h, k, l), paramDict)
+
+ def anglesToVirtualAngles(self, pos, wavelength):
+ """
+ Return dictionary of all virtual angles in degrees from Position object
+ in degrees and wavelength in Angstroms.
+ """
+ anglesDict = self._anglesToVirtualAngles(pos.inRadians(), wavelength)
+ for name in anglesDict:
+ anglesDict[name] = anglesDict[name] * TODEG
+ return anglesDict
+
+ def hklToAngles(self, h, k, l, wavelength):
+ """
+ Return verified Position and all virtual angles in degrees from
+ h, k & l and wavelength in Angstroms.
+
+ The calculated Position is verified by checking that it maps back using
+ anglesToHkl() to the requested hkl value.
+
+ Those virtual angles fixed or generated while calculating the position
+ are verified by by checking that they map back using
+ anglesToVirtualAngles to the virtual angles for the given position.
+
+ Throws a DiffcalcException if either check fails and
+ raiseExceptionsIfAnglesDoNotMapBackToHkl is True, otherwise displays a
+ warning.
+ """
+
+ # Update tracked parameters. During this calculation parameter values
+ # will be read directly from self._parameters instead of via
+ # self.getParameter which would trigger another potentially time-costly
+ # position update.
+ self.parameter_manager.update_tracked()
+
+ pos, virtualAngles = self._hklToAngles(h, k, l, wavelength) # in rad
+
+ # to degrees:
+ pos.changeToDegrees()
+
+ for key, val in virtualAngles.items():
+ if val is not None:
+ virtualAngles[key] = val * TODEG
+
+ self._verify_pos_map_to_hkl(h, k, l, wavelength, pos)
+
+ virtualAnglesReadback = self._verify_virtual_angles(h, k, l, wavelength, pos, virtualAngles)
+
+ return pos, virtualAnglesReadback
+
+ def _verify_pos_map_to_hkl(self, h, k, l, wavelength, pos):
+ hkl, _ = self.anglesToHkl(pos, wavelength)
+ e = 0.001
+ if ((abs(hkl[0] - h) > e) or (abs(hkl[1] - k) > e) or
+ (abs(hkl[2] - l) > e)):
+ s = "ERROR: The angles calculated for hkl=(%f,%f,%f) were %s.\n" % (h, k, l, str(pos))
+ s += "Converting these angles back to hkl resulted in hkl="\
+ "(%f,%f,%f)" % (hkl[0], hkl[1], hkl[2])
+ if self.raiseExceptionsIfAnglesDoNotMapBackToHkl:
+ raise DiffcalcException(s)
+ else:
+ print s
+
+ def _verify_virtual_angles(self, h, k, l, wavelength, pos, virtualAngles):
+ # Check that the virtual angles calculated/fixed during the hklToAngles
+ # those read back from pos using anglesToVirtualAngles
+ virtualAnglesReadback = self.anglesToVirtualAngles(pos, wavelength)
+ for key, val in virtualAngles.items():
+ if val != None: # Some values calculated in some mode_selector
+ r = virtualAnglesReadback[key]
+ if ((differ(val, r, .00001) and differ(val, r + 360, .00001) and differ(val, r - 360, .00001))):
+ s = "ERROR: The angles calculated for hkl=(%f,%f,%f) with"\
+ " mode=%s were %s.\n" % (h, k, l, self.repr_mode(), str(pos))
+ s += "During verification the virtual angle %s resulting "\
+ "from (or set for) this calculation of %f" % (key, val)
+ s += "did not match that calculated by "\
+ "anglesToVirtualAngles of %f" % virtualAnglesReadback[key]
+ if self.raiseExceptionsIfAnglesDoNotMapBackToHkl:
+ raise DiffcalcException(s)
+ else:
+ print s
+
+ return virtualAnglesReadback
+
+ def repr_mode(self):
+ pass
+
+### Collect all math access to context here
+
+ def _getUBMatrix(self):
+ return self._ubcalc.UB
+
+ def _getMode(self):
+ return self.mode_selector.getMode()
+
+ def _getSigma(self):
+ return self._ubcalc.sigma
+
+ def _getTau(self):
+ return self._ubcalc.tau
+
+ def _getParameter(self, name):
+ # Does not use context.getParameter as this will trigger a costly
+ # parameter collection
+ pm = self.parameter_manager
+ return pm.getParameterWithoutUpdatingTrackedParemeters(name)
+
+ def _getGammaParameterName(self):
+ return self._gammaParameterName
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/common.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/common.py
new file mode 100755
index 0000000..0b3c13a
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/hkl/common.py
@@ -0,0 +1,55 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+from diffcalc.util import allnum
+
+def getNameFromScannableOrString(o):
+ try: # it may be a scannable
+ return o.getName()
+ except AttributeError:
+ return str(o)
+ raise TypeError()
+
+
+class DummyParameterManager(object):
+
+ def getParameterDict(self):
+ return {}
+
+ def _setParameter(self, name, value):
+ raise KeyError(name)
+
+ def _getParameter(self, name):
+ raise KeyError(name)
+
+ def update_tracked(self):
+ pass
+
+
+def sim(self, scn, hkl):
+ """sim hkl scn -- simulates moving scannable (not all)
+ """
+ if not isinstance(hkl, (tuple, list)):
+ raise TypeError
+
+ if not allnum(hkl):
+ raise TypeError()
+
+ try:
+ print scn.simulateMoveTo(hkl)
+ except AttributeError:
+ raise TypeError("The first argument does not support simulated moves")
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/__init__.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/calc.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/calc.py
new file mode 100755
index 0000000..215e31a
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/calc.py
@@ -0,0 +1,846 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from math import pi, asin, acos, sin, cos, sqrt, atan2, fabs, atan
+
+try:
+ from numpy import matrix
+ from numpy.linalg import norm
+except ImportError:
+ from numjy import matrix
+ from numjy.linalg import norm
+
+from diffcalc.hkl.calcbase import HklCalculatorBase
+from diffcalc.hkl.vlieg.transform import TransformCInRadians
+from diffcalc.util import dot3, cross3, bound, differ
+from diffcalc.hkl.vlieg.geometry import createVliegMatrices, \
+ createVliegsPsiTransformationMatrix, \
+ createVliegsSurfaceTransformationMatrices, calcPHI
+from diffcalc.hkl.vlieg.geometry import VliegPosition
+from diffcalc.hkl.vlieg.constraints import VliegParameterManager
+from diffcalc.hkl.vlieg.constraints import ModeSelector
+from diffcalc.ub.calc import PaperSpecificUbCalcStrategy
+
+
+TORAD = pi / 180
+TODEG = 180 / pi
+transformC = TransformCInRadians()
+
+
+PREFER_POSITIVE_CHI_SOLUTIONS = True
+
+I = matrix('1 0 0; 0 1 0; 0 0 1')
+y = matrix('0; 1; 0')
+
+
+def check(condition, ErrorOrStringOrCallable, *args):
+ """
+ fail = check(condition, ErrorOrString) -- if condition is false raises the
+ Exception passed in, or creates one from a string. If a callable function
+ is passed in this is called with any args specified and the thing returns
+ false.
+ """
+ # TODO: Remove (really nasty) check function
+ if condition == False:
+ if callable(ErrorOrStringOrCallable):
+ ErrorOrStringOrCallable(*args)
+ return False
+ elif isinstance(ErrorOrStringOrCallable, str):
+ raise Exception(ErrorOrStringOrCallable)
+ else: # assume input is an exception
+ raise ErrorOrStringOrCallable
+ return True
+
+
+def sign(x):
+ if x < 0:
+ return -1
+ else:
+ return 1
+
+
+def vliegAnglesToHkl(pos, wavelength, UBMatrix):
+ """
+ Returns hkl indices from pos object in radians.
+ """
+ wavevector = 2 * pi / wavelength
+
+ # Create transformation matrices
+ [ALPHA, DELTA, GAMMA, OMEGA, CHI, PHI] = createVliegMatrices(
+ pos.alpha, pos.delta, pos.gamma, pos.omega, pos.chi, pos.phi)
+
+ # Create the plane normal vector in the alpha axis coordinate frame
+ qa = ((DELTA * GAMMA) - ALPHA.I) * matrix([[0], [wavevector], [0]])
+
+ # Transform the plane normal vector from the alpha frame to reciprical
+ # lattice frame.
+ hkl = UBMatrix.I * PHI.I * CHI.I * OMEGA.I * qa
+
+ return hkl[0, 0], hkl[1, 0], hkl[2, 0]
+
+
+class VliegUbCalcStrategy(PaperSpecificUbCalcStrategy):
+
+ def calculate_q_phi(self, pos):
+
+ [ALPHA, DELTA, GAMMA, OMEGA, CHI, PHI] = createVliegMatrices(
+ pos.alpha, pos.delta, pos.gamma, pos.omega, pos.chi, pos.phi)
+
+ u1a = (DELTA * GAMMA - ALPHA.I) * y
+ u1p = PHI.I * CHI.I * OMEGA.I * u1a
+ return u1p
+
+
+class VliegHklCalculator(HklCalculatorBase):
+
+ def __init__(self, ubcalc, geometry, hardware,
+ raiseExceptionsIfAnglesDoNotMapBackToHkl=True):
+ r = raiseExceptionsIfAnglesDoNotMapBackToHkl
+ HklCalculatorBase.__init__(self, ubcalc, geometry, hardware,
+ raiseExceptionsIfAnglesDoNotMapBackToHkl=r)
+ self._gammaParameterName = ({'arm': 'gamma', 'base': 'oopgamma'}
+ [self._geometry.gamma_location])
+ self.mode_selector = ModeSelector(self._geometry, None,
+ self._gammaParameterName)
+ self.parameter_manager = VliegParameterManager(
+ self._geometry, self._hardware, self.mode_selector,
+ self._gammaParameterName)
+ self.mode_selector.setParameterManager(self.parameter_manager)
+
+ def __str__(self):
+ # should list paramemeters and indicate which are used in selected mode
+ result = "Available mode_selector:\n"
+ result += self.mode_selector.reportAvailableModes()
+ result += '\nCurrent mode:\n'
+ result += self.mode_selector.reportCurrentMode()
+ result += '\n\nParameters:\n'
+ result += self.parameter_manager.reportAllParameters()
+ return result
+
+ def _anglesToHkl(self, pos, wavelength):
+ """
+ Return hkl tuple from VliegPosition in radians and wavelength in
+ Angstroms.
+ """
+ return vliegAnglesToHkl(pos, wavelength, self._getUBMatrix())
+
+ def _anglesToVirtualAngles(self, pos, wavelength):
+ """
+ Return dictionary of all virtual angles in radians from VliegPosition
+ object win radians and wavelength in Angstroms. The virtual angles are:
+ Bin, Bout, azimuth and 2theta.
+ """
+
+ # Create transformation matrices
+ [ALPHA, DELTA, GAMMA, OMEGA, CHI, PHI] = createVliegMatrices(
+ pos.alpha, pos.delta, pos.gamma, pos.omega, pos.chi, pos.phi)
+ [SIGMA, TAU] = createVliegsSurfaceTransformationMatrices(
+ self._getSigma() * TORAD, self._getTau() * TORAD)
+
+ S = TAU * SIGMA
+ y_vector = matrix([[0], [1], [0]])
+
+ # Calculate Bin from equation 15:
+ surfacenormal_alpha = OMEGA * CHI * PHI * S * matrix([[0], [0], [1]])
+ incoming_alpha = ALPHA.I * y_vector
+ minusSinBetaIn = dot3(surfacenormal_alpha, incoming_alpha)
+ Bin = asin(bound(-minusSinBetaIn))
+
+ # Calculate Bout from equation 16:
+ # surfacenormal_alpha has just ben calculated
+ outgoing_alpha = DELTA * GAMMA * y_vector
+ sinBetaOut = dot3(surfacenormal_alpha, outgoing_alpha)
+ Bout = asin(bound(sinBetaOut))
+
+ # Calculate 2theta from equation 25:
+
+ cosTwoTheta = dot3(ALPHA * DELTA * GAMMA * y_vector, y_vector)
+ twotheta = acos(bound(cosTwoTheta))
+ psi = self._anglesToPsi(pos, wavelength)
+
+ return {'Bin': Bin, 'Bout': Bout, 'azimuth': psi, '2theta': twotheta}
+
+ def _hklToAngles(self, h, k, l, wavelength):
+ """
+ Return VliegPosition and virtual angles in radians from h, k & l and
+ wavelength in Angstroms. The virtual angles are those fixed or
+ generated while calculating the position: Bin, Bout and 2theta; and
+ azimuth in four and five circle modes.
+ """
+
+ if self._getMode().group in ("fourc", "fivecFixedGamma",
+ "fivecFixedAlpha"):
+ return self._hklToAnglesFourAndFiveCirclesModes(h, k, l,
+ wavelength)
+ elif self._getMode().group == "zaxis":
+ return self._hklToAnglesZaxisModes(h, k, l, wavelength)
+ else:
+ raise RuntimeError(
+ 'The current mode (%s) has an unrecognised group: %s.'
+ % (self._getMode().name, self._getMode().group))
+
+ def _hklToAnglesFourAndFiveCirclesModes(self, h, k, l, wavelength):
+ """
+ Return VliegPosition and virtual angles in radians from h, k & l and
+ wavelength in Angstrom for four and five circle modes. The virtual
+ angles are those fixed or generated while calculating the position:
+ Bin, Bout, 2theta and azimuth.
+ """
+
+ # Results in radians during calculations, returned in degreess
+ pos = VliegPosition(None, None, None, None, None, None)
+
+ # Normalise hkl
+ wavevector = 2 * pi / wavelength
+ hklNorm = matrix([[h], [k], [l]]) / wavevector
+
+ # Compute hkl in phi axis coordinate frame
+ hklPhiNorm = self._getUBMatrix() * hklNorm
+
+ # Determine Bin and Bout
+ if self._getMode().name == '4cPhi':
+ Bin = Bout = None
+ else:
+ Bin, Bout = self._determineBinAndBoutInFourAndFiveCirclesModes(
+ hklNorm)
+
+ # Determine alpha and gamma
+ if self._getMode().group == 'fourc':
+ pos.alpha, pos.gamma = \
+ self._determineAlphaAndGammaForFourCircleModes(hklPhiNorm)
+ else:
+ pos.alpha, pos.gamma = \
+ self._determineAlphaAndGammaForFiveCircleModes(Bin, hklPhiNorm)
+ if pos.alpha < -pi:
+ pos.alpha += 2 * pi
+ if pos.alpha > pi:
+ pos.alpha -= 2 * pi
+
+ # Determine delta
+ (pos.delta, twotheta) = self._determineDelta(hklPhiNorm, pos.alpha,
+ pos.gamma)
+
+ # Determine omega, chi & phi
+ pos.omega, pos.chi, pos.phi, psi = \
+ self._determineSampleAnglesInFourAndFiveCircleModes(
+ hklPhiNorm, pos.alpha, pos.delta, pos.gamma, Bin)
+ # (psi will be None in fixed phi mode)
+
+ # Ensure that by default omega is between -90 and 90, by possibly
+ # transforming the sample angles
+ if self._getMode().name != '4cPhi': # not in fixed-phi mode
+ if pos.omega < -pi / 2 or pos.omega > pi / 2:
+ pos = transformC.transform(pos)
+
+ # Gather up the virtual angles calculated along the way...
+ # -pi pi:
+ psi -= 2 * pi
+ if psi < (-1 * pi):
+ psi += 2 * pi
+
+ v = {'2theta': twotheta, 'Bin': Bin, 'Bout': Bout, 'azimuth': psi}
+ return pos, v
+
+ def _hklToAnglesZaxisModes(self, h, k, l, wavelength):
+ """
+ Return VliegPosition and virtual angles in radians from h, k & l and
+ wavelength in Angstroms for z-axis modes. The virtual angles are those
+ fixed or generated while calculating the position: Bin, Bout, and
+ 2theta.
+ """
+ # Section 6:
+
+ # Results in radians during calculations, returned in degreess
+ pos = VliegPosition(None, None, None, None, None, None)
+
+ # Normalise hkl
+ wavevector = 2 * pi / wavelength
+ hkl = matrix([[h], [k], [l]])
+ hklNorm = hkl * (1.0 / wavevector)
+
+ # Compute hkl in phi axis coordinate frame
+ hklPhi = self._getUBMatrix() * hkl
+ hklPhiNorm = self._getUBMatrix() * hklNorm
+
+ # Determine Chi and Phi (Equation 29):
+ pos.phi = -self._getTau() * TORAD
+ pos.chi = -self._getSigma() * TORAD
+
+ # Equation 30:
+ [ALPHA, DELTA, GAMMA, OMEGA, CHI, PHI] = createVliegMatrices(
+ None, None, None, None, pos.chi, pos.phi)
+ del ALPHA, DELTA, GAMMA, OMEGA
+ Hw = CHI * PHI * hklPhi
+
+ # Determine Bin and Bout:
+ (Bin, Bout) = self._determineBinAndBoutInZaxisModes(
+ Hw[2, 0] / wavevector)
+
+ # Determine Alpha and Gamma (Equation 32):
+ pos.alpha = Bin
+ pos.gamma = Bout
+
+ # Determine Delta:
+ (pos.delta, twotheta) = self._determineDelta(hklPhiNorm, pos.alpha,
+ pos.gamma)
+
+ # Determine Omega:
+ delta = pos.delta
+ gamma = pos.gamma
+ d1 = (Hw[1, 0] * sin(delta) * cos(gamma) - Hw[0, 0] *
+ (cos(delta) * cos(gamma) - cos(pos.alpha)))
+ d2 = (Hw[0, 0] * sin(delta) * cos(gamma) + Hw[1, 0] *
+ (cos(delta) * cos(gamma) - cos(pos.alpha)))
+
+ if fabs(d2) < 1e-30:
+ pos.omega = sign(d1) * sign(d2) * pi / 2.0
+ else:
+ pos.omega = atan2(d1, d2)
+
+ # Gather up the virtual angles calculated along the way
+ return pos, {'2theta': twotheta, 'Bin': Bin, 'Bout': Bout}
+
+###
+
+ def _determineBinAndBoutInFourAndFiveCirclesModes(self, hklNorm):
+ """(Bin, Bout) = _determineBinAndBoutInFourAndFiveCirclesModes()"""
+ BinModes = ('4cBin', '5cgBin', '5caBin')
+ BoutModes = ('4cBout', '5cgBout', '5caBout')
+ BeqModes = ('4cBeq', '5cgBeq', '5caBeq')
+ azimuthModes = ('4cAzimuth')
+ fixedBusingAndLeviWmodes = ('4cFixedw')
+
+ # Calculate RHS of equation 20
+ # RHS (1/K)(S^-1*U*B*H)_3 where H/K = hklNorm
+ UB = self._getUBMatrix()
+ [SIGMA, TAU] = createVliegsSurfaceTransformationMatrices(
+ self._getSigma() * TORAD, self._getTau() * TORAD)
+ #S = SIGMA * TAU
+ S = TAU * SIGMA
+ RHS = (S.I * UB * hklNorm)[2, 0]
+
+ if self._getMode().name in BinModes:
+ Bin = self._getParameter('betain')
+ check(Bin != None, "The parameter betain must be set for mode %s" %
+ self._getMode().name)
+ Bin = Bin * TORAD
+ sinBout = RHS - sin(Bin)
+ check(fabs(sinBout) <= 1, "Could not compute Bout")
+ Bout = asin(sinBout)
+
+ elif self._getMode().name in BoutModes:
+ Bout = self._getParameter('betaout')
+ check(Bout != None, "The parameter Bout must be set for mode %s" %
+ self._getMode().name)
+ Bout = Bout * TORAD
+ sinBin = RHS - sin(Bout)
+ check(fabs(sinBin) <= 1, "Could not compute Bin")
+ Bin = asin(sinBin)
+
+ elif self._getMode().name in BeqModes:
+ sinBeq = RHS / 2
+ check(fabs(sinBeq) <= 1, "Could not compute Bin=Bout")
+ Bin = Bout = asin(sinBeq)
+
+ elif self._getMode().name in azimuthModes:
+ azimuth = self._getParameter('azimuth')
+ check(azimuth != None, "The parameter azimuth must be set for "
+ "mode %s" % self._getMode().name)
+ del azimuth
+ # TODO: codeit
+ raise NotImplementedError()
+
+ elif self._getMode().name in fixedBusingAndLeviWmodes:
+ bandlomega = self._getParameter('blw')
+ check(bandlomega != None, "The parameter abandlomega must be set "
+ "for mode %s" % self._getMode().name)
+ del bandlomega
+ # TODO: codeit
+ raise NotImplementedError()
+ else:
+ raise RuntimeError("AngleCalculator does not know how to handle "
+ "mode %s" % self._getMode().name)
+
+ return (Bin, Bout)
+
+ def _determineBinAndBoutInZaxisModes(self, Hw3OverK):
+ """(Bin, Bout) = _determineBinAndBoutInZaxisModes(HwOverK)"""
+ BinModes = ('6czBin')
+ BoutModes = ('6czBout')
+ BeqModes = ('6czBeq')
+
+ if self._getMode().name in BinModes:
+ Bin = self._getParameter('betain')
+ check(Bin != None, "The parameter betain must be set for mode %s" %
+ self._getMode().name)
+ Bin = Bin * TORAD
+ # Equation 32a:
+ Bout = asin(Hw3OverK - sin(Bin))
+
+ elif self._getMode().name in BoutModes:
+ Bout = self._getParameter('betaout')
+ check(Bout != None, "The parameter Bout must be set for mode %s" %
+ self._getMode().name)
+ Bout = Bout * TORAD
+ # Equation 32b:
+ Bin = asin(Hw3OverK - sin(Bout))
+
+ elif self._getMode().name in BeqModes:
+ # Equation 32c:
+ Bin = Bout = asin(Hw3OverK / 2)
+
+ return (Bin, Bout)
+
+###
+
+ def _determineAlphaAndGammaForFourCircleModes(self, hklPhiNorm):
+
+ if self._getMode().group == 'fourc':
+ alpha = self._getParameter('alpha') * TORAD
+ gamma = self._getParameter(self._getGammaParameterName()) * TORAD
+ check(alpha != None, "alpha parameter must be set in fourc modes")
+ check(gamma != None, "gamma parameter must be set in fourc modes")
+ return alpha, gamma
+ else:
+ raise RuntimeError(
+ "determineAlphaAndGammaForFourCirclesModes() "
+ "is not appropriate for %s modes" % self._getMode().group)
+
+ def _determineAlphaAndGammaForFiveCircleModes(self, Bin, hklPhiNorm):
+
+ ## Solve equation 34 for one possible Y, Yo
+ # Calculate surface normal in phi frame
+ [SIGMA, TAU] = createVliegsSurfaceTransformationMatrices(
+ self._getSigma() * TORAD, self._getTau() * TORAD)
+ S = TAU * SIGMA
+ surfaceNormalPhi = S * matrix([[0], [0], [1]])
+ # Compute beta in vector
+ BetaVector = matrix([[0], [-sin(Bin)], [cos(Bin)]])
+ # Find Yo
+ Yo = self._findMatrixToTransformAIntoB(surfaceNormalPhi, BetaVector)
+
+ ## Calculate Hv from equation 39
+ Z = matrix([[1, 0, 0],
+ [0, cos(Bin), sin(Bin)],
+ [0, -sin(Bin), cos(Bin)]])
+ Hv = Z * Yo * hklPhiNorm
+ # Fixed gamma:
+ if self._getMode().group == 'fivecFixedGamma':
+ gamma = self._getParameter(self._getGammaParameterName())
+ check(gamma != None,
+ "gamma parameter must be set in fivecFixedGamma modes")
+ gamma = gamma * TORAD
+ H2 = (hklPhiNorm[0, 0] ** 2 + hklPhiNorm[1, 0] ** 2 +
+ hklPhiNorm[2, 0] ** 2)
+ a = -(0.5 * H2 * sin(Bin) - Hv[2, 0])
+ b = -(1.0 - 0.5 * H2) * cos(Bin)
+ c = cos(Bin) * sin(gamma)
+ check((b * b + a * a - c * c) >= 0, 'Could not solve for alpha')
+ alpha = 2 * atan2(-(b + sqrt(b * b + a * a - c * c)), -(a + c))
+
+ # Fixed Alpha:
+ elif self._getMode().group == 'fivecFixedAlpha':
+ alpha = self._getParameter('alpha')
+ check(alpha != None,
+ "alpha parameter must be set in fivecFixedAlpha modes")
+ alpha = alpha * TORAD
+ H2 = (hklPhiNorm[0, 0] ** 2 + hklPhiNorm[1, 0] ** 2 +
+ hklPhiNorm[2, 0] ** 2)
+ t0 = ((2 * cos(alpha) * Hv[2, 0] - sin(Bin) * cos(alpha) * H2 +
+ cos(Bin) * sin(alpha) * H2 - 2 * cos(Bin) * sin(alpha)) /
+ (cos(Bin) * 2.0))
+ check(abs(t0) <= 1, "Cannot compute gamma: sin(gamma)>1")
+ gamma = asin(t0)
+ else:
+ raise RuntimeError(
+ "determineAlphaAndGammaInFiveCirclesModes() is not "
+ "appropriate for %s modes" % self._getMode().group)
+
+ return (alpha, gamma)
+
+###
+
+ def _determineDelta(self, hklPhiNorm, alpha, gamma):
+ """
+ (delta, twotheta) = _determineDelta(hklPhiNorm, alpha, gamma) --
+ computes delta for all modes. Also returns twotheta for sanity
+ checking. hklPhiNorm is a 3X1 matrix.
+
+ alpha, gamma & delta - in radians.
+ h k & l normalised to wavevector and in phi axis coordinates
+ """
+ h = hklPhiNorm[0, 0]
+ k = hklPhiNorm[1, 0]
+ l = hklPhiNorm[2, 0]
+ # See Vlieg section 5 (with K=1)
+ cosdelta = ((1 + sin(gamma) * sin(alpha) - (h * h + k * k + l * l) / 2)
+ / (cos(gamma) * cos(alpha)))
+ costwotheta = (cos(alpha) * cos(gamma) * bound(cosdelta) -
+ sin(alpha) * sin(gamma))
+ return (acos(bound(cosdelta)), acos(bound(costwotheta)))
+
+ def _determineSampleAnglesInFourAndFiveCircleModes(self, hklPhiNorm, alpha,
+ delta, gamma, Bin):
+ """
+ (omega, chi, phi, psi)=determineNonZAxisSampleAngles(hklPhiNorm, alpha,
+ delta, gamma, sigma, tau) where hkl has been normalised by the
+ wavevector and is in the phi Axis coordinate frame. All angles in
+ radians. hklPhiNorm is a 3X1 matrix
+ """
+
+ def equation49through59(psi):
+ # equation 49 R = (D^-1)*PI*D*Ro
+ PSI = createVliegsPsiTransformationMatrix(psi)
+ R = D.I * PSI * D * Ro
+
+ # eq 57: extract omega from R
+ if abs(R[0, 2]) < 1e-20:
+ omega = -sign(R[1, 2]) * sign(R[0, 2]) * pi / 2
+ else:
+ omega = -atan2(R[1, 2], R[0, 2])
+
+ # eq 58: extract chi from R
+ sinchi = sqrt(pow(R[0, 2], 2) + pow(R[1, 2], 2))
+ sinchi = bound(sinchi)
+ check(abs(sinchi) <= 1, 'could not compute chi')
+ # (there are two roots to this equation, but only the first is also
+ # a solution to R33=cos(chi))
+ chi = asin(sinchi)
+
+ # eq 59: extract phi from R
+ if abs(R[2, 0]) < 1e-20:
+ phi = sign(R[2, 1]) * sign(R[2, 1]) * pi / 2
+ else:
+ phi = atan2(-R[2, 1], -R[2, 0])
+ return omega, chi, phi
+
+ def checkSolution(omega, chi, phi):
+ _, _, _, OMEGA, CHI, PHI = createVliegMatrices(
+ None, None, None, omega, chi, phi)
+ R = OMEGA * CHI * PHI
+ RtimesH_phi = R * H_phi
+ print ("R*H_phi=%s, Q_alpha=%s" %
+ (R * H_phi.tolist(), Q_alpha.tolist()))
+ return not differ(RtimesH_phi, Q_alpha, .0001)
+
+ # Using Vlieg section 7.2
+
+ # Needed througout:
+ [ALPHA, DELTA, GAMMA, _, _, _] = createVliegMatrices(
+ alpha, delta, gamma, None, None, None)
+
+ ## Find Ro, one possible solution to equation 46: R*H_phi=Q_alpha
+
+ # Normalise hklPhiNorm (As it is currently normalised only to the
+ # wavevector)
+ normh = norm(hklPhiNorm)
+ check(normh >= 1e-10, "reciprical lattice vector too close to zero")
+ H_phi = hklPhiNorm * (1 / normh)
+
+ # Create Q_alpha from equation 47, (it comes normalised)
+ Q_alpha = ((DELTA * GAMMA) - ALPHA.I) * matrix([[0], [1], [0]])
+ Q_alpha = Q_alpha * (1 / norm(Q_alpha))
+
+ if self._getMode().name == '4cPhi':
+ ### Use the fixed value of phi as the final constraint ###
+ phi = self._getParameter('phi') * TORAD
+ PHI = calcPHI(phi)
+ H_chi = PHI * H_phi
+ omega, chi = _findOmegaAndChiToRotateHchiIntoQalpha(H_chi, Q_alpha)
+ return (omega, chi, phi, None) # psi = None as not calculated
+ else:
+ ### Use Bin as the final constraint ###
+
+ # Find a solution Ro to Ro*H_phi=Q_alpha
+ Ro = self._findMatrixToTransformAIntoB(H_phi, Q_alpha)
+
+ ## equation 50: Find a solution D to D*Q=norm(Q)*[[1],[0],[0]])
+ D = self._findMatrixToTransformAIntoB(
+ Q_alpha, matrix([[1], [0], [0]]))
+
+ ## Find psi and create PSI
+
+ # eq 54: compute u=D*Ro*S*[[0],[0],[1]], the surface normal in
+ # psi frame
+ [SIGMA, TAU] = createVliegsSurfaceTransformationMatrices(
+ self._getSigma() * TORAD, self._getTau() * TORAD)
+ S = TAU * SIGMA
+ [u1], [u2], [u3] = (D * Ro * S * matrix([[0], [0], [1]])).tolist()
+ # TODO: If u points along 100, then any psi is a solution. Choose 0
+ if not differ([u1, u2, u3], [1, 0, 0], 1e-9):
+ psi = 0
+ omega, chi, phi = equation49through59(psi)
+ else:
+ # equation 53: V=A*(D^-1)
+ V = ALPHA * D.I
+ v21 = V[1, 0]
+ v22 = V[1, 1]
+ v23 = V[1, 2]
+ # equation 55
+ a = v22 * u2 + v23 * u3
+ b = v22 * u3 - v23 * u2
+ c = -sin(Bin) - v21 * u1 # TODO: changed sign from paper
+
+ # equation 44
+ # Try first root:
+ def myatan2(y, x):
+ if abs(x) < 1e-20 and abs(y) < 1e-20:
+ return pi / 2
+ else:
+ return atan2(y, x)
+ psi = 2 * myatan2(-(b - sqrt(b * b + a * a - c * c)), -(a + c))
+ #psi = -acos(c/sqrt(a*a+b*b))+atan2(b,a)# -2*pi
+ omega, chi, phi = equation49through59(psi)
+
+ # if u points along z axis, the psi could have been either 0 or 180
+ if (not differ([u1, u2, u3], [0, 0, 1], 1e-9) and
+ abs(psi - pi) < 1e-10):
+ # Choose 0 to match that read up by angles-to-virtual-angles
+ psi = 0.
+ # if u points a long
+ return (omega, chi, phi, psi)
+
+ def _anglesToPsi(self, pos, wavelength):
+ """
+ pos assumed in radians. -180<= psi <= 180
+ """
+ # Using Vlieg section 7.2
+
+ # Needed througout:
+ [ALPHA, DELTA, GAMMA, OMEGA, CHI, PHI] = createVliegMatrices(
+ pos.alpha, pos.delta, pos.gamma, pos.omega, pos.chi, pos.phi)
+
+ # Solve equation 49 for psi, the rotation of the a reference solution
+ # about Qalpha or H_phi##
+
+ # Find Ro, the reference solution to equation 46: R*H_phi=Q_alpha
+
+ # Create Q_alpha from equation 47, (it comes normalised)
+ Q_alpha = ((DELTA * GAMMA) - ALPHA.I) * matrix([[0], [1], [0]])
+ Q_alpha = Q_alpha * (1 / norm(Q_alpha))
+
+ # Finh H_phi
+ h, k, l = self._anglesToHkl(pos, wavelength)
+ H_phi = self._getUBMatrix() * matrix([[h], [k], [l]])
+ normh = norm(H_phi)
+ check(normh >= 1e-10, "reciprical lattice vector too close to zero")
+ H_phi = H_phi * (1 / normh)
+
+ # Find a solution Ro to Ro*H_phi=Q_alpha
+ # This the reference solution with zero azimuth (psi)
+ Ro = self._findMatrixToTransformAIntoB(H_phi, Q_alpha)
+
+ # equation 48:
+ R = OMEGA * CHI * PHI
+
+ ## equation 50: Find a solution D to D*Q=norm(Q)*[[1],[0],[0]])
+ D = self._findMatrixToTransformAIntoB(Q_alpha, matrix([[1], [0], [0]]))
+
+ # solve equation 49 for psi
+ # D*R = PSI*D*Ro
+ # D*R*(D*Ro)^-1 = PSI
+ PSI = D * R * ((D * Ro).I)
+
+ # Find psi within PSI as defined in equation 51
+ PSI_23 = PSI[1, 2]
+ PSI_33 = PSI[2, 2]
+ psi = atan2(PSI_23, PSI_33)
+
+ #print "PSI: ", PSI.tolist()
+ return psi
+
+ def _findMatrixToTransformAIntoB(self, a, b):
+ """
+ Finds a particular matrix Mo that transforms the unit vector a into the
+ unit vector b. Thats is it finds Mo Mo*a=b. a and b 3x1 matrixes and Mo
+ is a 3x3 matrix.
+
+ Throws an exception if this is not possible.
+ """
+ # Maths from the appendix of "Angle caluculations
+ # for a 5-circle diffractometer used for surface X-ray diffraction",
+ # E. Vlieg, J.F. van der Veen, J.E. Macdonald and M. Miller, J. of
+ # Applied Cryst. 20 (1987) 330.
+ # - courtesy of Elias Vlieg again
+
+ # equation A2: compute angle xi between vectors a and b
+ cosxi = dot3(a, b)
+ try:
+ cosxi = bound(cosxi)
+ except ValueError:
+ raise Exception("Could not compute cos(xi), vectors a=%f and b=%f "
+ "must be of unit length" % (norm(a), norm(b)))
+ xi = acos(cosxi)
+
+ # Mo is identity matrix if xi zero (math below would blow up)
+ if abs(xi) < 1e-10:
+ return I
+
+ # equation A3: c=cross(a,b)/sin(xi)
+ c = cross3(a, b) * (1 / sin(xi))
+
+ # equation A4: find D matrix that transforms a into the frame
+ # x = a; y = c x a; z = c. */
+ a1 = a[0, 0]
+ a2 = a[1, 0]
+ a3 = a[2, 0]
+ c1 = c[0, 0]
+ c2 = c[1, 0]
+ c3 = c[2, 0]
+ D = matrix([[a1, a2, a3],
+ [c2 * a3 - c3 * a2, c3 * a1 - c1 * a3, c1 * a2 - c2 * a1],
+ [c1, c2, c3]])
+
+ # equation A5: create Xi to rotate by xi about z-axis
+ XI = matrix([[cos(xi), -sin(xi), 0],
+ [sin(xi), cos(xi), 0],
+ [0, 0, 1]])
+
+ # eq A6: compute Mo
+ return D.I * XI * D
+
+
+def _findOmegaAndChiToRotateHchiIntoQalpha(h_chi, q_alpha):
+ """
+ (omega, chi) = _findOmegaAndChiToRotateHchiIntoQalpha(H_chi, Q_alpha)
+
+ Solves for omega and chi in OMEGA*CHI*h_chi = q_alpha where h_chi and
+ q_alpha are 3x1 matrices with unit length. Omega and chi are returned in
+ radians.
+
+ Throws an exception if this is not possible.
+ """
+
+ def solve(a, b, c):
+ """
+ x1,x2 = solve(a , b, c)
+ solves for the two solutions to x in equations of the form
+ a*sin(x) + b*cos(x) = c
+ by using the trigonometric identity
+ a*sin(x) + b*cos(x) = a*sin(x)+b*cos(x)=sqrt(a**2+b**2)-sin(x+p)
+ where
+ p = atan(b/a) + {0 if a>=0
+ {pi if a<0
+ """
+ if a == 0:
+ p = pi / 2 if b >= 0 else - pi / 2
+ else:
+ p = atan(b / a)
+ if a < 0:
+ p = p + pi
+ guts = c / sqrt(a ** 2 + b ** 2)
+ if guts < -1:
+ guts = -1
+ elif guts > 1:
+ guts = 1
+ left1 = asin(guts)
+ left2 = pi - left1
+ return (left1 - p, left2 - p)
+
+ def ne(a, b):
+ """
+ shifts a and b in between -pi and pi and tests for near equality
+ """
+ def shift(a):
+ if a > pi:
+ return a - 2 * pi
+ elif a <= -pi:
+ return a + 2 * pi
+ else:
+ return a
+ return abs(shift(a) - shift(b)) < .0000001
+
+ # 1. Compute some solutions
+ h_chi1 = h_chi[0, 0]
+ h_chi2 = h_chi[1, 0]
+ h_chi3 = h_chi[2, 0]
+ q_alpha1 = q_alpha[0, 0]
+ q_alpha2 = q_alpha[1, 0]
+ q_alpha3 = q_alpha[2, 0]
+
+ try:
+ # a) Solve for chi using Equation 3
+ chi1, chi2 = solve(-h_chi1, h_chi3, q_alpha3)
+
+ # b) Solve for omega Equation 1 and each chi
+ B = h_chi1 * cos(chi1) + h_chi3 * sin(chi1)
+ eq1omega11, eq1omega12 = solve(h_chi2, B, q_alpha1)
+ B = h_chi1 * cos(chi2) + h_chi3 * sin(chi2)
+ eq1omega21, eq1omega22 = solve(h_chi2, B, q_alpha1)
+
+ # c) Solve for omega Equation 2 and each chi
+ A = -h_chi1 * cos(chi1) - h_chi3 * sin(chi1)
+ eq2omega11, eq2omega12 = solve(A, h_chi2, q_alpha2)
+ A = -h_chi1 * cos(chi2) - h_chi3 * sin(chi2)
+ eq2omega21, eq2omega22 = solve(A, h_chi2, q_alpha2)
+
+ except ValueError, e:
+ raise ValueError(
+ str(e) + ":\nProblem in fixed-phi calculation for:\nh_chi: " +
+ str(h_chi.tolist()) + " q_alpha: " + str(q_alpha.tolist()))
+
+ # 2. Choose values of chi and omega that are solutions to equations 1 and 2
+ solutions = []
+ # a) Check the chi1 solutions
+ print "_findOmegaAndChiToRotateHchiIntoQalpha:"
+ if ne(eq1omega11, eq2omega11) or ne(eq1omega11, eq2omega12):
+# print "1: eq1omega11, chi1 = ", eq1omega11, chi1
+ solutions.append((eq1omega11, chi1))
+ if ne(eq1omega12, eq2omega11) or ne(eq1omega12, eq2omega12):
+# print "2: eq1omega12, chi1 = ", eq1omega12, chi1
+ solutions.append((eq1omega12, chi1))
+ # b) Check the chi2 solutions
+ if ne(eq1omega21, eq2omega21) or ne(eq1omega21, eq2omega22):
+# print "3: eq1omega21, chi2 = ", eq1omega21, chi2
+ solutions.append((eq1omega21, chi2))
+ if ne(eq1omega22, eq2omega21) or ne(eq1omega22, eq2omega22):
+# print "4: eq1omega22, chi2 = ", eq1omega22, chi2
+ solutions.append((eq1omega22, chi2))
+# print solutions
+# print "*"
+
+ if len(solutions) == 0:
+ e = "h_chi: " + str(h_chi.tolist())
+ e += " q_alpha: " + str(q_alpha.tolist())
+ e += ("\nchi1:%4f eq1omega11:%4f eq1omega12:%4f eq2omega11:%4f "
+ "eq2omega12:%4f" % (chi1 * TODEG, eq1omega11 * TODEG,
+ eq1omega12 * TODEG, eq2omega11 * TODEG, eq2omega12 * TODEG))
+ e += ("\nchi2:%4f eq1omega21:%4f eq1omega22:%4f eq2omega21:%4f "
+ "eq2omega22:%4f" % (chi2 * TODEG, eq1omega21 * TODEG,
+ eq1omega22 * TODEG, eq2omega21 * TODEG, eq2omega22 * TODEG))
+ raise Exception("Could not find simultaneous solution for this fixed "
+ "phi mode problem\n" + e)
+
+ if not PREFER_POSITIVE_CHI_SOLUTIONS:
+ return solutions[0]
+
+ positive_chi_solutions = [sol for sol in solutions if sol[1] > 0]
+
+ if len(positive_chi_solutions) == 0:
+ print "WARNING: A +ve chi solution was requested, but none were found."
+ print " Returning a -ve one. Try the mapper"
+ return solutions[0]
+
+ if len(positive_chi_solutions) > 1:
+ print ("INFO: Multiple +ve chi solutions were found [(omega, chi) ...]"
+ " = " + str(positive_chi_solutions))
+ print " Returning the first"
+
+ return positive_chi_solutions[0]
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/constraints.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/constraints.py
new file mode 100755
index 0000000..4751bef
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/constraints.py
@@ -0,0 +1,336 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from copy import copy
+
+from diffcalc.util import DiffcalcException
+
+
+class Mode(object):
+
+ def __init__(self, index, name, group, description, parameterNames,
+ implemented=True):
+ self.index = index
+ self.group = group
+ self.name = name
+ self.description = description
+ self.parameterNames = parameterNames
+ self.implemented = implemented
+
+ def __repr__(self):
+ return "%i) %s" % (self.index, self.description)
+
+ def __str__(self):
+ return self.__repr__()
+
+ def usesParameter(self, name):
+ return name in self.parameterNames
+
+
+class ModeSelector(object):
+
+ def __init__(self, geometry, parameterManager=None,
+ gammaParameterName='gamma'):
+ self.parameter_manager = parameterManager
+ self._geometry = geometry
+ self._gammaParameterName = gammaParameterName
+ self._modelist = {} # indexed by non-contiguous mode number
+ self._configureAvailableModes()
+ self._selectedIndex = 1
+
+ def setParameterManager(self, manager):
+ """
+ Required as a ParameterManager and ModelSelector are mutually tied
+ together in practice
+ """
+ self.parameter_manager = manager
+
+ def _configureAvailableModes(self):
+
+ gammaName = self._gammaParameterName
+
+ ml = self._modelist
+
+ ml[0] = Mode(0, '4cFixedw', 'fourc', 'fourc fixed-bandlw',
+ ['alpha', gammaName, 'blw'], False)
+
+ ml[1] = Mode(1, '4cBeq', 'fourc', 'fourc bisecting',
+ ['alpha', gammaName])
+
+ ml[2] = Mode(2, '4cBin', 'fourc', 'fourc incoming',
+ ['alpha', gammaName, 'betain'])
+
+ ml[3] = Mode(3, '4cBout', 'fourc', 'fourc outgoing',
+ ['alpha', gammaName, 'betaout'])
+
+ ml[4] = Mode(4, '4cAzimuth', 'fourc', 'fourc azimuth',
+ ['alpha', gammaName, 'azimuth'], False)
+
+ ml[5] = Mode(5, '4cPhi', 'fourc', 'fourc fixed-phi',
+ ['alpha', gammaName, 'phi'])
+
+ ml[10] = Mode(10, '5cgBeq', 'fivecFixedGamma', 'fivec bisecting',
+ [gammaName])
+
+ ml[11] = Mode(11, '5cgBin', 'fivecFixedGamma', 'fivec incoming',
+ [gammaName, 'betain'])
+
+ ml[12] = Mode(12, '5cgBout', 'fivecFixedGamma', 'fivec outgoing',
+ [gammaName, 'betaout'])
+
+ ml[13] = Mode(13, '5caBeq', 'fivecFixedAlpha', 'fivec bisecting',
+ ['alpha'])
+
+ ml[14] = Mode(14, '5caBin', 'fivecFixedAlpha', 'fivec incoming',
+ ['alpha', 'betain'])
+
+ ml[15] = Mode(15, '5caBout', 'fivecFixedAlpha', 'fivec outgoing',
+ ['alpha', 'betaout'])
+
+ ml[20] = Mode(20, '6czBeq', 'zaxis', 'zaxis bisecting',
+ [])
+
+ ml[21] = Mode(21, '6czBin', 'zaxis', 'zaxis incoming',
+ ['betain'])
+
+ ml[22] = Mode(22, '6czBout', 'zaxis', 'zaxiz outgoing',
+ ['betaout'])
+
+ def setModeByIndex(self, index):
+ if index in self._modelist:
+ self._selectedIndex = index
+ else:
+ raise DiffcalcException("mode %r is not defined" % index)
+
+ def setModeByName(self, name):
+ def findModeWithName(name):
+ for index, mode in self._modelist.items():
+ if mode.name == name:
+ return index, mode
+ raise ValueError
+
+ try:
+ index, mode = findModeWithName(name)
+ except ValueError:
+ raise DiffcalcException(
+ 'Unknown mode. The diffraction calculator supports these '
+ 'modeSelector: %s' % self._supportedModes.keys())
+ if self._geometry.supports_mode_group(mode.group):
+ self._selectedIndex = index
+ else:
+ raise DiffcalcException(
+ "Mode %s not supported for this diffractometer (%s)." %
+ (name, self._geometry.name))
+
+ def getMode(self):
+ return self._modelist[self._selectedIndex]
+
+ def reportCurrentMode(self):
+ return self.getMode().__str__()
+
+ def reportAvailableModes(self):
+ result = ''
+ indecis = self._modelist.keys()
+ indecis.sort()
+ for index in indecis:
+ mode = self._modelist[index]
+ if self._geometry.supports_mode_group(mode.group):
+ paramString = ''
+ flags = ''
+ pm = self.parameter_manager
+ for paramName in pm.getUserChangableParametersForMode(mode):
+ paramString += paramName + ", "
+ if paramString:
+ paramString = paramString[:-2] # remove trailing commas
+ if not mode.implemented:
+ flags += "(Not impl.)"
+ result += ('%2i) %-15s (%s) %s\n' % (mode.index,
+ mode.description, paramString, flags))
+ return result
+
+
+class VliegParameterManager(object):
+
+ def __init__(self, geometry, hardware, modeSelector,
+ gammaParameterName='gamma'):
+ self._geometry = geometry
+ self._hardware = hardware
+ self._modeSelector = modeSelector
+ self._gammaParameterName = gammaParameterName
+ self._parameters = {}
+ self._defineParameters()
+
+ def _defineParameters(self):
+ # Set default fixed values (In degrees if angles)
+ self._parameters = {}
+ self._parameters['alpha'] = 0
+ self._parameters[self._gammaParameterName] = 0
+ self._parameters['blw'] = None # Busing and Levi omega!
+ self._parameters['betain'] = None
+ self._parameters['betaout'] = None
+ self._parameters['azimuth'] = None
+ self._parameters['phi'] = None
+
+ self._parameterDisplayOrder = (
+ 'alpha', self._gammaParameterName, 'betain', 'betaout', 'azimuth',
+ 'phi', 'blw')
+ self._trackableParameters = ('alpha', self._gammaParameterName, 'phi')
+ self._trackedParameters = []
+
+ # Overide parameters that are unchangable for this diffractometer
+ for (name, value) in self._geometry.fixed_parameters.items():
+ if name not in self._parameters:
+ raise RuntimeError(
+ "The %s diffractometer geometry specifies a fixed "
+ "parameter %s that is not used by the diffractometer "
+ "calculator" % (self._geometry.getName, name))
+ self._parameters[name] = value
+
+ def reportAllParameters(self):
+ self.update_tracked()
+ result = ''
+ for name in self._parameterDisplayOrder:
+ flags = ""
+ if not self._modeSelector.getMode().usesParameter(name):
+ flags += '(not relevant in this mode)'
+ if self._geometry.parameter_fixed(name):
+ flags += ' (fixed by this diffractometer)'
+ if self.isParameterTracked(name):
+ flags += ' (tracking hardware)'
+ value = self._parameters[name]
+ if value is None:
+ value = '---'
+ else:
+ value = float(value)
+ result += '%s: %s %s\n' % (name.rjust(8), value, flags)
+ return result
+
+ def reportParametersUsedInCurrentMode(self):
+ self.update_tracked()
+ result = ''
+ for name in self.getUserChangableParametersForMode(
+ self._modeSelector.getMode()):
+ flags = ""
+ value = self._parameters[name]
+ if value is None:
+ value = '---'
+ else:
+ value = float(value)
+ if self.isParameterTracked(name):
+ flags += ' (tracking hardware)'
+ result += '%s: %s %s\n' % (name.rjust(8), value, flags)
+ return result
+
+ def getUserChangableParametersForMode(self, mode=None):
+ """
+ (p1,p2...p3) = getUserChangableParametersForMode(mode) returns a list
+ of parameters names used in this mode for this diffractometer geometry.
+ Checks current mode if no mode specified.
+ """
+ if mode is None:
+ mode = self._mode
+ result = []
+ for name in self._parameterDisplayOrder:
+ if self._isParameterChangeable(name, mode):
+ result += [name]
+ return result
+
+### Fixed parameters stuff ###
+
+ def set_constraint(self, name, value):
+ if not name in self._parameters:
+ raise DiffcalcException("No fixed parameter %s is used by the "
+ "diffraction calculator" % name)
+ if self._geometry.parameter_fixed(name):
+ raise DiffcalcException(
+ "The parameter %s cannot be changed: It has been fixed by the "
+ "%s diffractometer geometry"
+ % (name, self._geometry.name))
+ if self.isParameterTracked(name):
+ # for safety and to avoid confusion:
+ raise DiffcalcException(
+ "Cannot change parameter %s as it is set to track an axis.\n"
+ "To turn this off use a command like 'trackalpha 0'." % name)
+
+ if not self.isParameterUsedInSelectedMode(name):
+ print ("WARNING: The parameter %s is not used in mode %i" %
+ (name, self._modeSelector.getMode().index))
+ self._parameters[name] = value
+
+ def isParameterUsedInSelectedMode(self, name):
+ return self._modeSelector.getMode().usesParameter(name)
+
+ def getParameterWithoutUpdatingTrackedParemeters(self, name):
+ try:
+ return self._parameters[name]
+ except KeyError:
+ raise DiffcalcException("No fixed parameter %s is used by the "
+ "diffraction calculator" % name)
+
+ def get_constraint(self, name):
+ self.update_tracked()
+ return self.getParameterWithoutUpdatingTrackedParemeters(name)
+
+ def getParameterDict(self):
+ self.update_tracked()
+ return copy(self._parameters)
+
+ @property
+ def settable_constraint_names(self):
+ """list of all available constraints that have settable values"""
+ return sorted(self.getParameterDict().keys())
+
+ def setTrackParameter(self, name, switch):
+ if not name in self._parameters.keys():
+ raise DiffcalcException("No fixed parameter %s is used by the "
+ "diffraction calculator" % name)
+ if not name in self._trackableParameters:
+ raise DiffcalcException("Parameter %s is not trackable" % name)
+ if not self._isParameterChangeable(name):
+ print ("WARNING: Parameter %s is not used in mode %i" %
+ (name, self._mode.index))
+ if switch:
+ if name not in self._trackedParameters:
+ self._trackedParameters.append(name)
+ else:
+ if name in self._trackedParameters:
+ self._trackedParameters.remove(name)
+
+ def isParameterTracked(self, name):
+ return (name in self._trackedParameters)
+
+ def update_tracked(self):
+ """Note that the name of a tracked parameter MUST map into the name of
+ an external diffractometer angle
+ """
+ if self._trackedParameters:
+ externalAnglePositionArray = self._hardware.get_position()
+ externalAngleNames = list(self._hardware.get_axes_names())
+ for name in self._trackedParameters:
+ self._parameters[name] = \
+ externalAnglePositionArray[externalAngleNames.index(name)]
+
+ def _isParameterChangeable(self, name, mode=None):
+ """
+ Returns true if parameter is used in a mode (current mode if none
+ specified), AND if it is not locked by the diffractometer geometry
+ """
+ if mode is None:
+ mode = self._modeSelector.getMode()
+ return (mode.usesParameter(name) and
+ not self._geometry.parameter_fixed(name))
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/geometry.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/geometry.py
new file mode 100755
index 0000000..fc26a83
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/geometry.py
@@ -0,0 +1,523 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from math import tan, cos, sin, asin, atan, pi, fabs
+
+try:
+ from numpy import matrix
+except ImportError:
+ from numjy import matrix
+
+from diffcalc.util import x_rotation, z_rotation, y_rotation
+from diffcalc.util import AbstractPosition
+from diffcalc.util import bound, nearlyEqual
+
+
+TORAD = pi / 180
+TODEG = 180 / pi
+
+
+def calcALPHA(alpha):
+ return x_rotation(alpha)
+
+
+def calcDELTA(delta):
+ return z_rotation(-delta)
+
+
+def calcGAMMA(gamma):
+ return x_rotation(gamma)
+
+
+def calcOMEGA(omega):
+ return z_rotation(-omega)
+
+
+def calcCHI(chi):
+ return y_rotation(chi)
+
+
+def calcPHI(phi):
+ return z_rotation(-phi)
+
+
+def createVliegMatrices(alpha=None, delta=None, gamma=None, omega=None,
+ chi=None, phi=None):
+
+ ALPHA = None if alpha is None else calcALPHA(alpha)
+ DELTA = None if delta is None else calcDELTA(delta)
+ GAMMA = None if gamma is None else calcGAMMA(gamma)
+ OMEGA = None if omega is None else calcOMEGA(omega)
+ CHI = None if chi is None else calcCHI(chi)
+ PHI = None if phi is None else calcPHI(phi)
+ return ALPHA, DELTA, GAMMA, OMEGA, CHI, PHI
+
+
+def createVliegsSurfaceTransformationMatrices(sigma, tau):
+ """[SIGMA, TAU] = createVliegsSurfaceTransformationMatrices(sigma, tau)
+ angles in radians
+ """
+ SIGMA = matrix([[cos(sigma), 0, sin(sigma)],
+ [0, 1, 0], \
+ [-sin(sigma), 0, cos(sigma)]])
+
+ TAU = matrix([[cos(tau), sin(tau), 0],
+ [-sin(tau), cos(tau), 0],
+ [0, 0, 1]])
+ return(SIGMA, TAU)
+
+
+def createVliegsPsiTransformationMatrix(psi):
+ """PSI = createPsiTransformationMatrices(psi)
+ angles in radians
+ """
+ return matrix([[1, 0, 0],
+ [0, cos(psi), sin(psi)],
+ [0, -sin(psi), cos(psi)]])
+
+
+class VliegPosition(AbstractPosition):
+ """The position of all six diffractometer axis"""
+ def __init__(self, alpha=None, delta=None, gamma=None, omega=None,
+ chi=None, phi=None):
+ self.alpha = alpha
+ self.delta = delta
+ self.gamma = gamma
+ self.omega = omega
+ self.chi = chi
+ self.phi = phi
+
+ def clone(self):
+ return VliegPosition(self.alpha, self.delta, self.gamma, self.omega,
+ self.chi, self.phi)
+
+ def changeToRadians(self):
+ self.alpha *= TORAD
+ self.delta *= TORAD
+ self.gamma *= TORAD
+ self.omega *= TORAD
+ self.chi *= TORAD
+ self.phi *= TORAD
+
+ def changeToDegrees(self):
+ self.alpha *= TODEG
+ self.delta *= TODEG
+ self.gamma *= TODEG
+ self.omega *= TODEG
+ self.chi *= TODEG
+ self.phi *= TODEG
+
+ def inRadians(self):
+ pos = self.clone()
+ pos.changeToRadians()
+ return pos
+
+ def inDegrees(self):
+ pos = self.clone()
+ pos.changeToDegrees()
+ return pos
+
+ def nearlyEquals(self, pos2, maxnorm):
+ for a, b in zip(self.totuple(), pos2.totuple()):
+ if abs(a - b) > maxnorm:
+ return False
+ return True
+
+ def totuple(self):
+ return (self.alpha, self.delta, self.gamma, self.omega,
+ self.chi, self.phi)
+
+ def __str__(self):
+ return ("VliegPosition(alpha %r delta: %r gamma: %r omega: %r chi: %r"
+ " phi: %r)" % self.totuple())
+
+ def __repr__(self):
+ return self.__str__()
+
+ def __eq__(self, b):
+ return self.nearlyEquals(b, .001)
+
+
+class VliegGeometry(object):
+
+# Required methods
+
+ def __init__(self, name, supported_mode_groups, fixed_parameters,
+ gamma_location):
+ """
+ Set geometry name (String), list of supported mode groups (list of
+ strings), list of axis names (list of strings). Define the parameters
+ e.g. alpha and gamma for a four circle (dictionary). Define wether the
+ gamma angle is on the 'arm' or the 'base'; used only by AngleCalculator
+ to interpret the gamma parameter in fixed gamma mode: for instruments
+ with gamma on the base, rather than on the arm as the code assume
+ internally, the two methods physical_angles_to_internal_position and
+ internal_position_to_physical_angles must still be used.
+ """
+ if gamma_location not in ('arm', 'base', None):
+ raise RuntimeError(
+ "Gamma must be on either 'arm' or 'base' or None")
+
+ self.name = name
+ self.supported_mode_groups = supported_mode_groups
+ self.fixed_parameters = fixed_parameters
+ self.gamma_location = gamma_location
+
+ def physical_angles_to_internal_position(self, physicalAngles):
+ raise NotImplementedError()
+
+ def internal_position_to_physical_angles(self, physicalAngles):
+ raise NotImplementedError()
+
+### Do not overide these these ###
+
+ def supports_mode_group(self, name):
+ return name in self.supported_mode_groups
+
+ def parameter_fixed(self, name): # parameter_fixed
+ return name in self.fixed_parameters.keys()
+
+
+class SixCircleGammaOnArmGeometry(VliegGeometry):
+ """
+ This six-circle diffractometer geometry simply passes through the
+ angles from a six circle diffractometer with the same geometry and
+ angle names as those defined in Vliegs's paper defined internally.
+ """
+
+ def __init__(self):
+ VliegGeometry.__init__(
+ self,
+ name='sixc_gamma_on_arm',
+ supported_mode_groups=('fourc', 'fivecFixedGamma',
+ 'fivecFixedAlpha', 'zaxis'),
+ fixed_parameters={},
+ gamma_location='arm')
+
+ def physical_angles_to_internal_position(self, physicalAngles):
+ """ (a,d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
+ """
+ assert (len(physicalAngles) == 6), "Wrong length of input list"
+ return VliegPosition(*physicalAngles)
+
+ def internal_position_to_physical_angles(self, internalPosition):
+ """ (a,d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
+ """
+ return internalPosition.totuple()
+
+
+class SixCircleGeometry(VliegGeometry):
+ """
+ This six-circle diffractometer geometry simply passes through the
+ angles from a six circle diffractometer with the same geometry and
+ angle names as those defined in Vliegs's paper defined internally.
+ """
+
+ def __init__(self):
+ VliegGeometry.__init__(
+ self,
+ name='sixc',
+ supported_mode_groups=('fourc', 'fivecFixedGamma',
+ 'fivecFixedAlpha', 'zaxis'),
+ fixed_parameters={},
+ gamma_location='base')
+ self.hardwareMonitor = None
+#(deltaA, gammaA) = gammaOnBaseToArm(deltaB, gammaB, alpha) (all in radians)
+#(deltaB, gammaB) = gammaOnArmToBase(deltaA, gammaA, alpha) (all in radians)
+
+ def physical_angles_to_internal_position(self, physicalAngles):
+ """ (a,d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
+ """
+ assert (len(physicalAngles) == 6), "Wrong length of input list"
+ alpha, deltaB, gammaB, omega, chi, phi = physicalAngles
+ (deltaA, gammaA) = gammaOnBaseToArm(
+ deltaB * TORAD, gammaB * TORAD, alpha * TORAD)
+ return VliegPosition(
+ alpha, deltaA * TODEG, gammaA * TODEG, omega, chi, phi)
+
+ def internal_position_to_physical_angles(self, internalPosition):
+ """ (a,d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
+ """
+ alpha, deltaA, gammaA, omega, chi, phi = internalPosition.totuple()
+ deltaB, gammaB = gammaOnArmToBase(
+ deltaA * TORAD, gammaA * TORAD, alpha * TORAD)
+ deltaB, gammaB = deltaB * TODEG, gammaB * TODEG
+
+ if self.hardwareMonitor is not None:
+ gammaName = self.hardwareMonitor.get_axes_names()[2]
+ minGamma = self.hardwareMonitor.get_lower_limit(gammaName)
+ maxGamma = self.hardwareMonitor.get_upper_limit(gammaName)
+
+ if maxGamma is not None:
+ if gammaB > maxGamma:
+ gammaB = gammaB - 180
+ deltaB = 180 - deltaB
+ if minGamma is not None:
+ if gammaB < minGamma:
+ gammaB = gammaB + 180
+ deltaB = 180 - deltaB
+
+ return alpha, deltaB, gammaB, omega, chi, phi
+
+
+class FivecWithGammaOnBase(SixCircleGeometry):
+
+ def __init__(self):
+ VliegGeometry.__init__(
+ self,
+ name='fivec_with_gamma',
+ supported_mode_groups=('fourc', 'fivecFixedGamma'),
+ fixed_parameters={'alpha': 0.0},
+ gamma_location='base')
+ self.hardwareMonitor = None
+
+ def physical_angles_to_internal_position(self, physicalAngles):
+ """ (a,d,g,o,c,p) = physicalAnglesToInternal(d,g,o,c,p)
+ """
+ assert (len(physicalAngles) == 5), "Wrong length of input list"
+ return SixCircleGeometry.physical_angles_to_internal_position(
+ self, (0,) + tuple(physicalAngles))
+
+ def internal_position_to_physical_angles(self, internalPosition):
+ """ (d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
+ """
+ return SixCircleGeometry.internal_position_to_physical_angles(
+ self, internalPosition)[1:]
+
+
+class Fivec(VliegGeometry):
+ """
+ This five-circle diffractometer geometry is for diffractometers with the
+ same geometry and angle names as those defined in Vliegs's paper defined
+ internally, but with no out plane detector arm gamma."""
+
+ def __init__(self):
+ VliegGeometry.__init__(self,
+ name='fivec',
+ supported_mode_groups=('fourc', 'fivecFixedGamma'),
+ fixed_parameters={'gamma': 0.0},
+ gamma_location='arm'
+ )
+
+ def physical_angles_to_internal_position(self, physicalAngles):
+ """ (a,d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
+ """
+ assert (len(physicalAngles) == 5), "Wrong length of input list"
+ physicalAngles = tuple(physicalAngles)
+ angles = physicalAngles[0:2] + (0.0,) + physicalAngles[2:]
+ return VliegPosition(*angles)
+
+ def internal_position_to_physical_angles(self, internalPosition):
+ """ (a,d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
+ """
+ sixAngles = internalPosition.totuple()
+ return sixAngles[0:2] + sixAngles[3:]
+
+
+class Fourc(VliegGeometry):
+ """
+ This five-circle diffractometer geometry is for diffractometers with the
+ same geometry and angle names as those defined in Vliegs's paper defined
+ internally, but with no out plane detector arm gamma."""
+
+ def __init__(self):
+ VliegGeometry.__init__(self,
+ name='fourc',
+ supported_mode_groups=('fourc'),
+ fixed_parameters={'gamma': 0.0, 'alpha': 0.0},
+ gamma_location='arm'
+ )
+
+ def physical_angles_to_internal_position(self, physicalAngles):
+ """ (a,d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
+ """
+ assert (len(physicalAngles) == 4), "Wrong length of input list"
+ physicalAngles = tuple(physicalAngles)
+ angles = (0.0, physicalAngles[0], 0.0) + physicalAngles[1:]
+ return VliegPosition(*angles)
+
+ def internal_position_to_physical_angles(self, internalPosition):
+ """ (a,d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
+ """
+ sixAngles = internalPosition.totuple()
+ return sixAngles[1:2] + sixAngles[3:]
+
+
+def sign(x):
+ if x < 0:
+ return -1
+ else:
+ return 1
+
+"""
+Based on: Elias Vlieg, "A (2+3)-Type Surface Diffractometer: Mergence of
+the z-axis and (2+2)-Type Geometries", J. Appl. Cryst. (1998). 31.
+198-203
+"""
+
+
+def solvesEq8(alpha, deltaA, gammaA, deltaB, gammaB):
+ tol = 1e-6
+ return (nearlyEqual(sin(deltaA) * cos(gammaA), sin(deltaB), tol) and
+ nearlyEqual(cos(deltaA) * cos(gammaA),
+ cos(gammaB - alpha) * cos(deltaB), tol) and
+ nearlyEqual(sin(gammaA), sin(gammaB - alpha) * cos(deltaB), tol))
+
+
+GAMMAONBASETOARM_WARNING = '''
+WARNING: This diffractometer has the gamma circle attached to the
+ base rather than the end of
+ the delta arm as Vlieg's paper defines. A conversion has
+ been made from the physical angles to their internal
+ representation (gamma-on-base-to-arm). This conversion has
+ forced gamma to be positive by applying the mapping:
+
+ delta --> 180+delta
+ gamma --> 180+gamma.
+
+ This should have no adverse effect.
+'''
+
+
+def gammaOnBaseToArm(deltaB, gammaB, alpha):
+ """
+ (deltaA, gammaA) = gammaOnBaseToArm(deltaB, gammaB, alpha) (all in
+ radians)
+
+ Maps delta and gamma for an instrument where the gamma circle rests on
+ the base to the case where it is on the delta arm.
+
+ There are always two possible solutions. To get the second apply the
+ transform:
+
+ delta --> 180+delta (flip to opposite side of circle)
+ gamma --> 180+gamma (flip to opposite side of circle)
+
+ This code will return the solution where gamma is between 0 and 180.
+ """
+
+ ### Equation11 ###
+ if fabs(cos(gammaB - alpha)) < 1e-20:
+ deltaA1 = sign(tan(deltaB)) * sign(cos(gammaB - alpha)) * pi / 2
+ else:
+ deltaA1 = atan(tan(deltaB) / cos(gammaB - alpha))
+ # ...second root
+ if deltaA1 <= 0:
+ deltaA2 = deltaA1 + pi
+ else:
+ deltaA2 = deltaA1 - pi
+
+ ### Equation 12 ###
+ gammaA1 = asin(bound(cos(deltaB) * sin(gammaB - alpha)))
+ # ...second root
+ if gammaA1 >= 0:
+ gammaA2 = pi - gammaA1
+ else:
+ gammaA2 = -pi - gammaA1
+
+ # Choose the delta solution that fits equations 8
+ if solvesEq8(alpha, deltaA1, gammaA1, deltaB, gammaB):
+ deltaA, gammaA = deltaA1, gammaA1
+ elif solvesEq8(alpha, deltaA2, gammaA1, deltaB, gammaB):
+ deltaA, gammaA = deltaA2, gammaA1
+ print "gammaOnBaseToArm choosing 2nd delta root (to internal)"
+ elif solvesEq8(alpha, deltaA1, gammaA2, deltaB, gammaB):
+ print "gammaOnBaseToArm choosing 2nd gamma root (to internal)"
+ deltaA, gammaA = deltaA1, gammaA2
+ elif solvesEq8(alpha, deltaA2, gammaA2, deltaB, gammaB):
+ print "gammaOnBaseToArm choosing 2nd delta root and 2nd gamma root"
+ deltaA, gammaA = deltaA2, gammaA2
+ else:
+ raise RuntimeError(
+ "No valid solutions found mapping from gamma-on-base to gamma-on-arm")
+
+ return deltaA, gammaA
+
+GAMMAONARMTOBASE_WARNING = '''
+ WARNING: This diffractometer has the gamma circle attached to the base
+ rather than the end of the delta arm as Vlieg's paper defines.
+ A conversion has been made from the internal representation of
+ angles to physical angles (gamma-on-arm-to-base). This
+ conversion has forced gamma to be positive by applying the
+ mapping:
+
+ delta --> 180-delta
+ gamma --> 180+gamma.
+
+ This should have no adverse effect.
+'''
+
+
+def gammaOnArmToBase(deltaA, gammaA, alpha):
+ """
+ (deltaB, gammaB) = gammaOnArmToBase(deltaA, gammaA, alpha) (all in
+ radians)
+
+ Maps delta and gamma for an instrument where the gamma circle is on
+ the delta arm to the case where it rests on the base.
+
+ There are always two possible solutions. To get the second apply the
+ transform:
+
+ delta --> 180-delta (reflect and flip to opposite side)
+ gamma --> 180+gamma (flip to opposite side)
+
+ This code will return the solution where gamma is positive, but will
+ warn if a sign change was made.
+ """
+
+ ### Equation 9 ###
+ deltaB1 = asin(bound(sin(deltaA) * cos(gammaA)))
+ # ...second root:
+ if deltaB1 >= 0:
+ deltaB2 = pi - deltaB1
+ else:
+ deltaB2 = -pi - deltaB1
+
+ ### Equation 10 ###:
+ if fabs(cos(deltaA)) < 1e-20:
+ gammaB1 = sign(tan(gammaA)) * sign(cos(deltaA)) * pi / 2 + alpha
+ else:
+ gammaB1 = atan(tan(gammaA) / cos(deltaA)) + alpha
+ #... second root:
+ if gammaB1 <= 0:
+ gammaB2 = gammaB1 + pi
+ else:
+ gammaB2 = gammaB1 - pi
+
+ ### Choose the solution that fits equation 8 ###
+ if (solvesEq8(alpha, deltaA, gammaA, deltaB1, gammaB1) and
+ 0 <= gammaB1 <= pi):
+ deltaB, gammaB = deltaB1, gammaB1
+ elif (solvesEq8(alpha, deltaA, gammaA, deltaB2, gammaB1) and
+ 0 <= gammaB1 <= pi):
+ deltaB, gammaB = deltaB2, gammaB1
+ print "gammaOnArmToBase choosing 2nd delta root (to physical)"
+ elif (solvesEq8(alpha, deltaA, gammaA, deltaB1, gammaB2) and
+ 0 <= gammaB2 <= pi):
+ print "gammaOnArmToBase choosing 2nd gamma root (to physical)"
+ deltaB, gammaB = deltaB1, gammaB2
+ elif (solvesEq8(alpha, deltaA, gammaA, deltaB2, gammaB2)
+ and 0 <= gammaB2 <= pi):
+ print "gammaOnArmToBase choosing 2nd delta root and 2nd gamma root"
+ deltaB, gammaB = deltaB2, gammaB2
+ else:
+ raise RuntimeError(
+ "No valid solutions found mapping gamma-on-arm to gamma-on-base")
+
+ return deltaB, gammaB
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/hkl.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/hkl.py
new file mode 100755
index 0000000..ae05018
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/hkl.py
@@ -0,0 +1,139 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from diffcalc.hkl.common import getNameFromScannableOrString
+from diffcalc.util import command
+from diffcalc import settings
+
+
+from diffcalc.ub import ub
+from diffcalc.hkl.vlieg.calc import VliegHklCalculator
+
+
+__all__ = ['hklmode', 'setpar', 'trackalpha', 'trackgamma', 'trackphi',
+ 'parameter_manager', 'hklcalc']
+
+
+hklcalc = VliegHklCalculator(ub.ubcalc, settings.geometry, settings.hardware)
+
+parameter_manager = hklcalc.parameter_manager
+
+def __str__(self):
+ return hklcalc.__str__()
+
+@command
+def hklmode(num=None):
+ """hklmode {num} -- changes mode or shows current and available modes and all settings""" #@IgnorePep8
+
+ if num is None:
+ print hklcalc.__str__()
+ else:
+ hklcalc.mode_selector.setModeByIndex(int(num))
+ pm = hklcalc.parameter_manager
+ print (hklcalc.mode_selector.reportCurrentMode() + "\n" +
+ pm.reportParametersUsedInCurrentMode())
+
+def _setParameter(name, value):
+ hklcalc.parameter_manager.set_constraint(name, value)
+
+def _getParameter(name):
+ return hklcalc.parameter_manager.get_constraint(name)
+
+@command
+def setpar(scannable_or_string=None, val=None):
+ """setpar {parameter_scannable {{val}} -- sets or shows a parameter'
+ setpar {parameter_name {val}} -- sets or shows a parameter'
+ """
+
+ if scannable_or_string is None:
+ #show all
+ parameterDict = hklcalc.parameter_manager.getParameterDict()
+ names = parameterDict.keys()
+ names.sort()
+ for name in names:
+ print _representParameter(name)
+ else:
+ name = getNameFromScannableOrString(scannable_or_string)
+ if val is None:
+ _representParameter(name)
+ else:
+ oldval = _getParameter(name)
+ _setParameter(name, float(val))
+ print _representParameter(name, oldval, float(val))
+
+def _representParameter(name, oldval=None, newval=None):
+ flags = ''
+ if hklcalc.parameter_manager.isParameterTracked(name):
+ flags += '(tracking hardware) '
+ if settings.geometry.parameter_fixed(name): # @UndefinedVariable
+ flags += '(fixed by geometry) '
+ pm = hklcalc.parameter_manager
+ if not pm.isParameterUsedInSelectedMode(name):
+ flags += '(not relevant in this mode) '
+ if oldval is None:
+ val = _getParameter(name)
+ if val is None:
+ val = "---"
+ else:
+ val = str(val)
+ return "%s: %s %s" % (name, val, flags)
+ else:
+ return "%s: %s --> %f %s" % (name, oldval, newval, flags)
+
+def _checkInputAndSetOrShowParameterTracking(name, b=None):
+ """
+ for track-parameter commands: If no args displays parameter settings,
+ otherwise sets the tracking switch for the given parameter and displays
+ settings.
+ """
+ # set if arg given
+ if b is not None:
+ hklcalc.parameter_manager.setTrackParameter(name, b)
+ # Display:
+ lastValue = _getParameter(name)
+ if lastValue is None:
+ lastValue = "---"
+ else:
+ lastValue = str(lastValue)
+ flags = ''
+ if hklcalc.parameter_manager.isParameterTracked(name):
+ flags += '(tracking hardware)'
+ print "%s: %s %s" % (name, lastValue, flags)
+
+@command
+def trackalpha(b=None):
+ """trackalpha {boolean} -- determines wether alpha parameter will track alpha axis""" #@IgnorePep8
+ _checkInputAndSetOrShowParameterTracking('alpha', b)
+
+@command
+def trackgamma(b=None):
+ """trackgamma {boolean} -- determines wether gamma parameter will track alpha axis""" #@IgnorePep8
+ _checkInputAndSetOrShowParameterTracking('gamma', b)
+
+@command
+def trackphi(b=None):
+ """trackphi {boolean} -- determines wether phi parameter will track phi axis""" #@IgnorePep8
+ _checkInputAndSetOrShowParameterTracking('phi', b)
+
+
+commands_for_help = ['Mode',
+ hklmode,
+ setpar,
+ trackalpha,
+ trackgamma,
+ trackphi]
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/transform.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/transform.py
new file mode 100755
index 0000000..a41e8b9
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/transform.py
@@ -0,0 +1,480 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from diffcalc.util import command
+
+from copy import copy
+from math import pi
+
+from diffcalc.hkl.vlieg.geometry import VliegPosition as P
+
+SMALL = 1e-10
+
+
+class Transform(object):
+
+ def transform(self, pos):
+ raise RuntimeError('Not implemented')
+
+
+### Transforms, currently for definition and testing the theory only
+
+class TransformC(Transform):
+ '''Flip omega, invert chi and flip phi
+ '''
+ def transform(self, pos):
+ pos = pos.clone()
+ pos.omega -= 180
+ pos.chi *= -1
+ pos.phi -= 180
+ return pos
+
+
+class TransformB(Transform):
+ '''Flip chi, and invert and flip omega
+ '''
+ def transform(self, pos):
+ pos = pos.clone()
+ pos.chi -= 180
+ pos.omega = 180 - pos.omega
+ return pos
+
+
+class TransformA(Transform):
+ '''Invert scattering plane: invert delta and omega and flip chi'''
+ def transform(self, pos):
+ pos = pos.clone()
+ pos.delta *= -1
+ pos.omega *= -1
+ pos.chi -= 180
+ return pos
+
+
+class TransformCInRadians(Transform):
+ '''
+ Flip omega, invert chi and flip phi. Using radians and keeping
+ -pi 0:
+ pos.omega -= pi
+ else:
+ pos.omega += pi
+ pos.chi *= -1
+ pos.phi += pi
+ return pos
+
+
+###
+
+transformsFromSector = {
+ 0: (),
+ 1: ('c',),
+ 2: ('a',),
+ 3: ('a', 'c'),
+ 4: ('b', 'c'),
+ 5: ('b',),
+ 6: ('a', 'b', 'c'),
+ 7: ('a', 'b')
+}
+
+sectorFromTransforms = {}
+for k, v in transformsFromSector.iteritems():
+ sectorFromTransforms[v] = k
+
+
+class VliegPositionTransformer(object):
+
+ def __init__(self, geometry, hardware, solution_transformer):
+ self._geometry = geometry
+ self._hardware = hardware
+ self._solution_transformer = solution_transformer
+ solution_transformer.limitCheckerFunction = self.is_position_within_limits
+
+ def transform(self, pos):
+ # 1. Choose the correct sector/transforms
+ return self._solution_transformer.transformPosition(pos)
+
+ def is_position_within_limits(self, position):
+ '''where position is Position object in degrees'''
+ angleTuple = self._geometry.internal_position_to_physical_angles(position)
+ angleTuple = self._hardware.cut_angles(angleTuple)
+ return self._hardware.is_position_within_limits(angleTuple)
+
+
+class VliegTransformSelector(object):
+ '''All returned angles are between -180. and 180. -180.<=angle<180.
+ '''
+### basic sector selection
+
+ def __init__(self):
+ self.transforms = []
+ self.autotransforms = []
+ self.autosectors = []
+ self.limitCheckerFunction = None # inject
+ self.sector = None
+ self.setSector(0)
+
+ def setSector(self, sector):
+ if not 0 <= sector <= 7:
+ raise ValueError('%i must between 0 and 7.' % sector)
+ self.sector = sector
+ self.transforms = list(transformsFromSector[sector])
+
+ def setTransforms(self, transformList):
+ transformList = list(transformList)
+ transformList.sort()
+ self.sector = sectorFromTransforms[tuple(transformList)]
+ self.transforms = transformList
+
+ def addTransorm(self, transformName):
+ if transformName not in ('a', 'b', 'c'):
+ raise ValueError('%s is not a recognised transform. Try a, b or c'
+ % transformName)
+ if transformName in self.transforms:
+ print "WARNING, transform %s is already selected"
+ else:
+ self.setTransforms(self.transforms + [transformName])
+
+ def removeTransorm(self, transformName):
+ if transformName not in ('a', 'b', 'c'):
+ raise ValueError('%s is not a recognised transform. Try a, b or c'
+ % transformName)
+ if transformName in self.transforms:
+ new = copy(self.transforms)
+ new.remove(transformName)
+ self.setTransforms(new)
+ else:
+ print "WARNING, transform %s was not selected" % transformName
+
+ def addAutoTransorm(self, transformOrSector):
+ '''
+ If input is a string (letter), tags one of the transofrms as being a
+ candidate for auto application. If a number, tags a sector as being a
+ candidate for auto application, and removes similar tags for any
+ transforms (as the two are incompatable).
+ '''
+ if type(transformOrSector) == str:
+ transform = transformOrSector
+ if transform not in ('a', 'b', 'c'):
+ raise ValueError(
+ '%s is not a recognised transform. Try a, b or c' %
+ transform)
+ if transform not in self.autotransforms:
+ self.autosectors = []
+ self.autotransforms.append(transform)
+ else:
+ print "WARNING: %s is already set to auto apply" % transform
+ elif type(transformOrSector) == int:
+ sector = transformOrSector
+ if not 0 <= sector <= 7:
+ raise ValueError('%i must between 0 and 7.' % sector)
+ if sector not in self.autosectors:
+ self.autotransforms = []
+ self.autosectors.append(sector)
+ else:
+ print "WARNING: %i is already set to auto apply" % sector
+ else:
+ raise ValueError("Input must be 'a', 'b' or 'c', "
+ "or 1,2,3,4,5,6 or 7.")
+
+ def removeAutoTransform(self, transformOrSector):
+ if type(transformOrSector) == str:
+ transform = transformOrSector
+ if transform not in ('a', 'b', 'c'):
+ raise ValueError("%s is not a recognised transform. "
+ "Try a, b or c" % transform)
+ if transform in self.autotransforms:
+ self.autotransforms.remove(transform)
+ else:
+ print "WARNING: %s is not set to auto apply" % transform
+ elif type(transformOrSector) == int:
+ sector = transformOrSector
+ if not 0 <= sector <= 7:
+ raise ValueError('%i must between 0 and 7.' % sector)
+ if sector in self.autosectors:
+ self.autosectors.remove(sector)
+ else:
+ print "WARNING: %s is not set to auto apply" % sector
+ else:
+ raise ValueError("Input must be 'a', 'b' or 'c', "
+ "or 1,2,3,4,5,6 or 7.")
+
+ def setAutoSectors(self, sectorList):
+ for sector in sectorList:
+ if not 0 <= sector <= 7:
+ raise ValueError('%i must between 0 and 7.' % sector)
+ self.autosectors = list(sectorList)
+
+ def transformPosition(self, pos):
+ pos = self.transformNWithoutCut(self.sector, pos)
+ cutpos = self.cutPosition(pos)
+ # -180 <= cutpos < 180, NOT the externally applied cuts
+ if len(self.autosectors) > 0:
+ if self.is_position_within_limits(cutpos):
+ return cutpos
+ else:
+ return self.autoTransformPositionBySector(cutpos)
+ if len(self.autotransforms) > 0:
+ if self.is_position_within_limits(cutpos):
+ return cutpos
+ else:
+ return self.autoTransformPositionByTransforms(pos)
+ #else
+ return cutpos
+
+ def transformNWithoutCut(self, n, pos):
+
+ if n == 0:
+ return P(pos.alpha, pos.delta, pos.gamma,
+ pos.omega, pos.chi, pos.phi)
+ if n == 1:
+ return P(pos.alpha, pos.delta, pos.gamma,
+ pos.omega - 180., -pos.chi, pos.phi - 180.)
+ if n == 2:
+ return P(pos.alpha, -pos.delta, pos.gamma,
+ -pos.omega, pos.chi - 180., pos.phi)
+ if n == 3:
+ return P(pos.alpha, -pos.delta, pos.gamma,
+ 180. - pos.omega, 180. - pos.chi, pos.phi - 180.)
+ if n == 4:
+ return P(pos.alpha, pos.delta, pos.gamma,
+ -pos.omega, 180. - pos.chi, pos.phi - 180.)
+ if n == 5:
+ return P(pos.alpha, pos.delta, pos.gamma,
+ 180. - pos.omega, pos.chi - 180., pos.phi)
+ if n == 6:
+ return P(pos.alpha, -pos.delta, pos.gamma,
+ pos.omega, -pos.chi, pos.phi - 180.)
+ if n == 7:
+ return P(pos.alpha, -pos.delta, pos.gamma,
+ pos.omega - 180., pos.chi, pos.phi)
+ else:
+ raise Exception("sector must be between 0 and 7")
+
+### autosector
+
+ def hasAutoSectorsOrTransformsToApply(self):
+ return len(self.autosectors) > 0 or len(self.autotransforms) > 0
+
+ def autoTransformPositionBySector(self, pos):
+ okaysectors = []
+ okaypositions = []
+ for sector in self.autosectors:
+ newpos = self.transformNWithoutCut(sector, pos)
+ if self.is_position_within_limits(newpos):
+ okaysectors.append(sector)
+ okaypositions.append(newpos)
+ if len(okaysectors) == 0:
+ raise Exception(
+ "Autosector could not find a sector (from %s) to move %s into "
+ "limits." % (self.autosectors, str(pos)))
+ if len(okaysectors) > 1:
+ print ("WARNING: Autosector found multiple sectors that would "
+ "move %s to move into limits: %s" % (str(pos), okaysectors))
+
+ print ("INFO: Autosector changed sector from %i to %i" %
+ (self.sector, okaysectors[0]))
+ self.sector = okaysectors[0]
+ return okaypositions[0]
+
+ def autoTransformPositionByTransforms(self, pos):
+ possibleTransforms = self.createListOfPossibleTransforms()
+ okaytransforms = []
+ okaypositions = []
+ for transforms in possibleTransforms:
+ sector = sectorFromTransforms[tuple(transforms)]
+ newpos = self.cutPosition(self.transformNWithoutCut(sector, pos))
+ if self.is_position_within_limits(newpos):
+ okaytransforms.append(transforms)
+ okaypositions.append(newpos)
+ if len(okaytransforms) == 0:
+ raise Exception(
+ "Autosector could not find a sector (from %r) to move %r into "
+ "limits." % (self.autosectors, pos))
+ if len(okaytransforms) > 1:
+ print ("WARNING: Autosector found multiple sectors that would "
+ "move %s to move into limits: %s" %
+ (repr(pos), repr(okaytransforms)))
+
+ print ("INFO: Autosector changed selected transforms from %r to %r" %
+ (self.transforms, okaytransforms[0]))
+ self.setTransforms(okaytransforms[0])
+ return okaypositions[0]
+
+ def createListOfPossibleTransforms(self):
+ def vary(possibleTransforms, name):
+ result = []
+ for transforms in possibleTransforms:
+ # add the original.
+ result.append(transforms)
+ # add a modified one
+ toadd = list(copy(transforms))
+ if name in transforms:
+ toadd.remove(name)
+ else:
+ toadd.append(name)
+ toadd.sort()
+ result.append(toadd)
+ return result
+ # start with the currently selected list of transforms
+ if len(self.transforms) == 0:
+ possibleTransforms = [()]
+ else:
+ possibleTransforms = copy(self.transforms)
+
+ for name in self.autotransforms:
+ possibleTransforms = vary(possibleTransforms, name)
+
+ return possibleTransforms
+
+ def is_position_within_limits(self, pos):
+ '''where pos os a poistion object in degrees'''
+ return self.limitCheckerFunction(pos)
+
+ def __repr__(self):
+ def createPrefix(transform):
+ if transform in self.transforms:
+ s = '*on* '
+ else:
+ s = 'off '
+ if len(self.autotransforms) > 0:
+ if transform in self.autotransforms:
+ s += '*auto*'
+ else:
+ s += ' '
+ return s
+ s = 'Transforms/sector:\n'
+ s += (' %s (a transform) Invert scattering plane: invert delta and '
+ 'omega and flip chi\n' % createPrefix('a'))
+ s += (' %s (b transform) Flip chi, and invert and flip omega\n' %
+ createPrefix('b'))
+ s += (' %s (c transform) Flip omega, invert chi and flip phi\n' %
+ createPrefix('c'))
+ s += ' Current sector: %i (Spec fourc equivalent)\n' % self.sector
+ if len(self.autosectors) > 0:
+ s += ' Auto sectors: %s\n' % self.autosectors
+ return s
+
+ def cutPosition(self, position):
+ '''Cuts angles at -180.; moves each argument between -180. and 180.
+ '''
+ def cut(a):
+ if a is None:
+ return None
+ else:
+ if a < (-180. - SMALL):
+ return a + 360.
+ if a > (180. + SMALL):
+ return a - 360.
+ return a
+ return P(cut(position.alpha), cut(position.delta), cut(position.gamma),
+ cut(position.omega), cut(position.chi), cut(position.phi))
+
+
+def getNameFromScannableOrString(o):
+ try: # it may be a scannable
+ return o.getName()
+ except AttributeError:
+ return str(o)
+
+
+class TransformCommands(object):
+
+ def __init__(self, sector_selector):
+ self._sectorSelector = sector_selector
+
+ @command
+ def transform(self):
+ """transform -- show transform configuration"""
+ print self._sectorSelector.__repr__()
+
+ @command
+ def transforma(self, *args):
+ """transforma {on|off|auto|manual} -- configure transform A application
+ """
+ self._transform('transforma', 'a', args)
+
+ @command
+ def transformb(self, *args):
+ """transformb {on|off|auto|manual} -- configure transform B application
+ """
+ self._transform('transformb', 'b', args)
+
+ @command
+ def transformc(self, *args):
+ """transformc {on|off|auto|manual} -- configure transform C application
+ """
+
+ self._transform('transformc', 'c', args)
+
+ def _transform(self, commandName, transformName, args):
+ if len(args) == 0:
+ print self._sectorSelector.__repr__()
+ return
+ # get name
+ if len(args) != 1:
+ raise TypeError()
+ if type(args[0]) is not str:
+ raise TypeError()
+
+ ss = self._sectorSelector
+ if args[0] == 'on':
+ ss.addTransorm(transformName)
+ elif args[0] == 'off':
+ ss.removeTransorm(transformName)
+ elif args[0] == 'auto':
+ ss.addAutoTransorm(transformName)
+ elif args[0] == 'manual':
+ ss.removeAutoTransform(transformName)
+ else:
+ raise TypeError()
+ print self._sectorSelector.__repr__()
+
+ @command
+ def sector(self, sector=None):
+ """sector {0-7} -- Select or display sector (a la Spec)
+ """
+ if sector is None:
+ print self._sectorSelector.__repr__()
+ else:
+ if type(sector) is not int and not (0 <= sector <= 7):
+ raise TypeError()
+ self._sectorSelector.setSector(sector)
+ print self._sectorSelector.__repr__()
+
+ @command
+ def autosector(self, *args):
+ """autosector [None] [0-7] [0-7]... -- Set sectors that might be automatically applied""" #@IgnorePep8
+ if len(args) == 0:
+ print self._sectorSelector.__repr__()
+ elif len(args) == 1 and args[0] is None:
+ self._sectorSelector.setAutoSectors([])
+ print self._sectorSelector.__repr__()
+ else:
+ sectorList = []
+ for arg in args:
+ if type(arg) is not int:
+ raise TypeError()
+ sectorList.append(arg)
+ self._sectorSelector.setAutoSectors(sectorList)
+ print self._sectorSelector.__repr__()
+
+
+
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/__init__.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/calc.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/calc.py
new file mode 100755
index 0000000..a8e94ba
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/calc.py
@@ -0,0 +1,292 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from math import pi, asin, acos, atan2, sin, cos, sqrt
+
+try:
+ from numpy import matrix
+except ImportError:
+ from numjy import matrix
+
+from diffcalc.log import logging
+from diffcalc.util import bound, AbstractPosition, DiffcalcException,\
+ x_rotation, z_rotation
+from diffcalc.hkl.vlieg.geometry import VliegGeometry
+from diffcalc.ub.calc import PaperSpecificUbCalcStrategy
+from diffcalc.hkl.calcbase import HklCalculatorBase
+from diffcalc.hkl.common import DummyParameterManager
+
+logger = logging.getLogger("diffcalc.hkl.willmot.calcwill")
+
+CHOOSE_POSITIVE_GAMMA = True
+
+TORAD = pi / 180
+TODEG = 180 / pi
+I = matrix('1 0 0; 0 1 0; 0 0 1')
+SMALL = 1e-10
+
+TEMPORARY_CONSTRAINTS_DICT_RAD = {'betain': 2 * TORAD}
+
+
+def create_matrices(delta, gamma, omegah, phi):
+ return (calc_DELTA(delta), calc_GAMMA(gamma), calc_OMEGAH(omegah),
+ calc_PHI(phi))
+
+
+def calc_DELTA(delta):
+ return x_rotation(delta) # (39)
+
+
+def calc_GAMMA(gamma):
+ return z_rotation(gamma) # (40)
+
+
+def calc_OMEGAH(omegah):
+ return x_rotation(omegah) # (41)
+
+
+def calc_PHI(phi):
+ return z_rotation(phi) # (42)
+
+
+def angles_to_hkl_phi(delta, gamma, omegah, phi):
+ """Calculate hkl matrix in phi frame in units of 2*pi/lambda
+ """
+ DELTA, GAMMA, OMEGAH, PHI = create_matrices(delta, gamma, omegah, phi)
+ H_lab = (GAMMA * DELTA - I) * matrix([[0], [1], [0]]) # (43)
+ H_phi = PHI.I * OMEGAH.I * H_lab # (44)
+ return H_phi
+
+
+def angles_to_hkl(delta, gamma, omegah, phi, wavelength, UB):
+ """Calculate hkl matrix in reprical lattice space in units of 1/Angstrom
+ """
+ H_phi = angles_to_hkl_phi(delta, gamma, omegah, phi) * 2 * pi / wavelength
+ hkl = UB.I * H_phi # (5)
+ return hkl
+
+
+class WillmottHorizontalPosition(AbstractPosition):
+
+ def __init__(self, delta=None, gamma=None, omegah=None, phi=None):
+ self.delta = delta
+ self.gamma = gamma
+ self.omegah = omegah
+ self.phi = phi
+
+ def clone(self):
+ return WillmottHorizontalPosition(self.delta, self.gamma, self.omegah,
+ self.phi)
+
+ def changeToRadians(self):
+ self.delta *= TORAD
+ self.gamma *= TORAD
+ self.omegah *= TORAD
+ self.phi *= TORAD
+
+ def changeToDegrees(self):
+ self.delta *= TODEG
+ self.gamma *= TODEG
+ self.omegah *= TODEG
+ self.phi *= TODEG
+
+ def totuple(self):
+ return (self.delta, self.gamma, self.omegah, self.phi)
+
+ def __str__(self):
+ return ('WillmottHorizontalPosition('
+ 'delta: %.4f gamma: %.4f omegah: %.4f phi: %.4f)' %
+ (self.delta, self.gamma, self.omegah, self.phi))
+
+
+class WillmottHorizontalGeometry(object):
+
+ def __init__(self):
+ self.name = 'willmott_horizontal'
+
+ def physical_angles_to_internal_position(self, physicalAngles):
+ return WillmottHorizontalPosition(*physicalAngles)
+
+ def internal_position_to_physical_angles(self, internalPosition):
+ return internalPosition.totuple()
+
+ def create_position(self, delta, gamma, omegah, phi):
+ return WillmottHorizontalPosition(delta, gamma, omegah, phi)
+
+
+class WillmottHorizontalUbCalcStrategy(PaperSpecificUbCalcStrategy):
+
+ def calculate_q_phi(self, pos):
+ H_phi = angles_to_hkl_phi(*pos.totuple())
+ return matrix(H_phi.tolist())
+
+
+class DummyConstraints(object):
+
+ @property
+ def reference(self):
+ """dictionary of constrained reference circles"""
+ return TEMPORARY_CONSTRAINTS_DICT_RAD
+
+
+class ConstraintAdapter(object):
+
+ def __init__(self, constraints):
+ self._constraints = constraints
+
+ def getParameterDict(self):
+ names = self._constraints.available
+ return dict(zip(names, [None] * len(names)))
+
+ def setParameter(self, name, value):
+ self._constraints.set_constraint(name, value)
+
+ def get(self, name):
+ if name in self._constraints.all:
+ val = self._constraints.get_value(name)
+ return 999 if val is None else val
+ else:
+ return 999
+
+ def update_tracked(self):
+ pass
+
+
+class WillmottHorizontalCalculator(HklCalculatorBase):
+
+ def __init__(self, ubcalc, geometry, hardware, constraints,
+ raiseExceptionsIfAnglesDoNotMapBackToHkl=True):
+ """"
+ Where constraints.reference is a one element dict with the key either
+ ('betain', 'betaout' or 'equal') and the value a number or None for
+ 'betain_eq_betaout'
+ """
+
+ HklCalculatorBase.__init__(self, ubcalc, geometry, hardware,
+ raiseExceptionsIfAnglesDoNotMapBackToHkl)
+
+ if constraints is not None:
+ self.constraints = constraints
+ self.parameter_manager = ConstraintAdapter(constraints)
+ else:
+ self.constraints = DummyConstraints()
+ self.parameter_manager = DummyParameterManager()
+
+ @property
+ def _UB(self):
+ return self._ubcalc.UB
+
+ def _anglesToHkl(self, pos, wavelength):
+ """
+ Calculate miller indices from position in radians.
+ """
+ hkl_matrix = angles_to_hkl(pos.delta, pos.gamma, pos.omegah, pos.phi,
+ wavelength, self._UB)
+ return hkl_matrix[0, 0], hkl_matrix[1, 0], hkl_matrix[2, 0],
+
+ def _anglesToVirtualAngles(self, pos, wavelength):
+ """
+ Calculate virtual-angles in radians from position in radians.
+
+ Return theta, alpha, and beta in a dictionary.
+ """
+
+ betain = pos.omegah # (52)
+
+ hkl = angles_to_hkl(pos.delta, pos.gamma, pos.omegah, pos.phi,
+ wavelength, self._UB)
+ H_phi = self._UB * hkl
+ H_phi = H_phi / (2 * pi / wavelength)
+ l_phi = H_phi[2, 0]
+ sin_betaout = l_phi - sin(betain)
+ betaout = asin(bound(sin_betaout)) # (54)
+
+ cos_2theta = cos(pos.delta) * cos(pos.gamma)
+ theta = acos(bound(cos_2theta)) / 2.
+
+ return {'theta': theta, 'betain': betain, 'betaout': betaout}
+
+ def _hklToAngles(self, h, k, l, wavelength):
+ """
+ Calculate position and virtual angles in radians for a given hkl.
+ """
+
+ H_phi = self._UB * matrix([[h], [k], [l]]) # units: 1/Angstrom
+ H_phi = H_phi / (2 * pi / wavelength) # units: 2*pi/wavelength
+ h_phi = H_phi[0, 0]
+ k_phi = H_phi[1, 0]
+ l_phi = H_phi[2, 0] # (5)
+
+ ### determine betain (omegah) and betaout ###
+
+ if not self.constraints.reference:
+ raise ValueError("No reference constraint has been constrained.")
+
+ ref_name, ref_value = self.constraints.reference.items()[0]
+ if ref_value is not None:
+ ref_value *= TORAD
+ if ref_name == 'betain':
+ betain = ref_value
+ betaout = asin(bound(l_phi - sin(betain))) # (53)
+ elif ref_name == 'betaout':
+ betaout = ref_value
+ betain = asin(bound(l_phi - sin(betaout))) # (54)
+ elif ref_name == 'bin_eq_bout':
+ betain = betaout = asin(bound(l_phi / 2)) # (55)
+ else:
+ raise ValueError("Unexpected constraint name'%s'." % ref_name)
+
+ if abs(betain) < SMALL:
+ raise DiffcalcException('required betain was 0 degrees (requested '
+ 'q is perpendicular to surface normal)')
+ if betain < -SMALL:
+ raise DiffcalcException("betain was -ve (%.4f)" % betain)
+# logger.info('betain = %.4f, betaout = %.4f',
+# betain * TODEG, betaout * TODEG)
+ omegah = betain # (52)
+
+ ### determine H_lab (X, Y and Z) ###
+
+ Y = -(h_phi ** 2 + k_phi ** 2 + l_phi ** 2) / 2 # (45)
+
+ Z = (sin(betaout) + sin(betain) * (Y + 1)) / cos(omegah) # (47)
+
+ X_squared = (h_phi ** 2 + k_phi ** 2 -
+ ((cos(betain) * Y + sin(betain) * Z) ** 2)) # (48)
+ if (X_squared < 0) and (abs(X_squared) < SMALL):
+ X_squared = 0
+ Xpositive = sqrt(X_squared)
+ if CHOOSE_POSITIVE_GAMMA:
+ X = -Xpositive
+ else:
+ X = Xpositive
+# logger.info('H_lab (X,Y,Z) = [%.4f, %.4f, %.4f]', X, Y, Z)
+ ### determine diffractometer angles ###
+
+ gamma = atan2(-X, Y + 1) # (49)
+ if (abs(gamma) < SMALL):
+ # degenerate case, only occurs when q || z
+ delta = 2 * omegah
+ else:
+ delta = atan2(Z * sin(gamma), -X) # (50)
+ M = cos(betain) * Y + sin(betain) * Z
+ phi = atan2(h_phi * M - k_phi * X, h_phi * X + k_phi * M) # (51)
+
+ pos = WillmottHorizontalPosition(delta, gamma, omegah, phi)
+ virtual_angles = {'betain': betain, 'betaout': betaout}
+ return pos, virtual_angles
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/commands.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/commands.py
new file mode 100755
index 0000000..1d6cadd
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/commands.py
@@ -0,0 +1,58 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from diffcalc.hkl.common import getNameFromScannableOrString
+from diffcalc.util import command
+
+
+class WillmottHklCommands(object):
+
+ def __init__(self, hklcalc):
+ self._hklcalc = hklcalc
+ self.commands = [self.con,
+ self.uncon,
+ self.cons]
+
+ def __str__(self):
+ return self._hklcalc.__str__()
+
+ @command
+ def con(self, scn_or_string):
+ """con -- constrains constraint
+ """
+ name = getNameFromScannableOrString(scn_or_string)
+ self._hklcalc.constraints.constrain(name)
+ print self._report_constraints()
+
+ @command
+ def uncon(self, scn_or_string):
+ """uncon -- unconstrains constraint
+ """
+ name = getNameFromScannableOrString(scn_or_string)
+ self._hklcalc.constraints.unconstrain(name)
+ print self._report_constraints()
+
+ @command
+ def cons(self):
+ """cons -- list available constraints and values
+ """
+ print self._report_constraints()
+
+ def _report_constraints(self):
+ return (self._hklcalc.constraints.build_display_table_lines() + '\n\n' +
+ self._hklcalc.constraints._report_constraints())
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/constraints.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/constraints.py
new file mode 100755
index 0000000..9ab5abd
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/constraints.py
@@ -0,0 +1,156 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from diffcalc.util import DiffcalcException
+
+
+def filter_dict(d, keys):
+ """Return a copy of d containing only keys that are in keys"""
+ ##return {k: d[k] for k in keys} # requires Python 2.6
+ return dict((k, d[k]) for k in keys if k in d.keys())
+
+
+ref_constraints = ('betain', 'betaout', 'bin_eq_bout')
+valueless_constraints = ('bin_eq_bout')
+all_constraints = ref_constraints
+
+
+class WillmottConstraintManager(object):
+ """Constraints in degrees.
+ """
+
+ def __init__(self):
+ self._constrained = {'bin_eq_bout': None}
+
+ @property
+ def available_constraint_names(self):
+ """list of all available constraints"""
+ return all_constraints
+
+ @property
+ def all(self): # @ReservedAssignment
+ """dictionary of all constrained values"""
+ return self._constrained.copy()
+
+ @property
+ def reference(self):
+ """dictionary of constrained reference circles"""
+ return filter_dict(self.all, ref_constraints)
+
+ @property
+ def constrained_names(self):
+ """ordered tuple of constained circles"""
+ names = self.all.keys()
+ names.sort(key=lambda name: list(all_constraints).index(name))
+ return tuple(names)
+
+ def is_constrained(self, name):
+ return name in self._constrained
+
+ def get_value(self, name):
+ return self._constrained[name]
+
+ def _build_display_table(self):
+ constraint_types = (ref_constraints,)
+ num_rows = max([len(col) for col in constraint_types])
+ max_name_width = max(
+ [len(name) for name in sum(constraint_types[:2], ())])
+ # headings
+ lines = [' ' + 'REF'.ljust(max_name_width)]
+ lines.append(' ' + '=' * max_name_width + ' ')
+
+ # constraint rows
+ for n_row in range(num_rows):
+ cells = []
+ for col in constraint_types:
+ name = col[n_row] if n_row < len(col) else ''
+ cells.append(self._label_constraint(name))
+ cells.append(('%-' + str(max_name_width) + 's ') % name)
+ lines.append(''.join(cells))
+ lines.append
+ return '\n'.join(lines)
+
+ def _report_constraints(self):
+ if not self.reference:
+ return "!!! No reference constraint set"
+ name, val = self.reference.items()[0]
+ if name in valueless_constraints:
+ return " %s" % name
+ else:
+ if val is None:
+ return "!!! %s: ---" % name
+ else:
+ return " %s: %.4f" % (name, val)
+
+ def _label_constraint(self, name):
+ if name == '':
+ label = ' '
+ elif (self.is_constrained(name) and (self.get_value(name) is None) and
+ name not in valueless_constraints):
+ label = 'o-> '
+ elif self.is_constrained(name):
+ label = '--> '
+ else:
+ label = ' '
+ return label
+
+ def constrain(self, name):
+ if name in self.all:
+ return "%s is already constrained." % name.capitalize()
+ elif name in ref_constraints:
+ return self._constrain_reference(name)
+ else:
+ raise DiffcalcException('%s is not a valid constraint name')
+
+ def _constrain_reference(self, name):
+ if self.reference:
+ constrained_name = self.reference.keys()[0]
+ del self._constrained[constrained_name]
+ self._constrained[name] = None
+ return '%s constraint replaced.' % constrained_name.capitalize()
+ else:
+ self._constrained[name] = None
+
+ def unconstrain(self, name):
+ if name in self._constrained:
+ del self._constrained[name]
+ else:
+ return "%s was not already constrained." % name.capitalize()
+
+###
+ def _check_constraint_settable(self, name, verb):
+ if name not in all_constraints:
+ raise DiffcalcException(
+ 'Could not %(verb)s %(name)s as this is not an available '
+ 'constraint.' % locals())
+ elif name not in self.all.keys():
+ raise DiffcalcException(
+ 'Could not %(verb)s %(name)s as this is not currently '
+ 'constrained.' % locals())
+ elif name in valueless_constraints:
+ raise DiffcalcException(
+ 'Could not %(verb)s %(name)s as this constraint takes no '
+ 'value.' % locals())
+
+ def set_constraint(self, name, value): # @ReservedAssignment
+ self._check_constraint_settable(name, 'set')
+ old_value = self.all[name]
+ old = str(old_value) if old_value is not None else '---'
+ self._constrained[name] = float(value)
+ new = str(value)
+ return "%(name)s : %(old)s --> %(new)s" % locals()
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/you/__init__.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/you/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/you/calc.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/you/calc.py
new file mode 100755
index 0000000..5062f84
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/hkl/you/calc.py
@@ -0,0 +1,1162 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from math import pi, sin, cos, acos, asin, atan2, sqrt
+from itertools import product
+
+try:
+ from numpy import matrix
+ from numpy.linalg import norm
+except ImportError:
+ from numjy import matrix
+ from numjy.linalg import norm
+
+from diffcalc.log import logging
+from diffcalc.hkl.calcbase import HklCalculatorBase
+from diffcalc.hkl.you.geometry import create_you_matrices, calcMU, calcPHI, \
+ calcCHI, calcETA
+from diffcalc.hkl.you.geometry import YouPosition
+from diffcalc.util import DiffcalcException, bound, angle_between_vectors,\
+ y_rotation
+from diffcalc.util import cross3, z_rotation, x_rotation
+from diffcalc.ub.calc import PaperSpecificUbCalcStrategy
+
+from diffcalc.hkl.you.constraints import NUNAME
+logger = logging.getLogger("diffcalc.hkl.you.calc")
+I = matrix('1 0 0; 0 1 0; 0 0 1')
+
+SMALL = 1e-6
+TORAD = pi / 180
+TODEG = 180 / pi
+
+PRINT_DEGENERATE = False
+
+
+def is_small(x):
+ return abs(x) < SMALL
+
+
+def sign(x):
+ if is_small(x):
+ return 0
+ if x > 0:
+ return 1
+ # x < 0
+ return -1
+
+
+def normalised(vector):
+ return vector * (1 / norm(vector))
+
+
+def cut_at_minus_pi(value):
+ if value < (-pi - SMALL):
+ return value + 2 * pi
+ if value >= pi + SMALL:
+ return value - 2 * pi
+ return value
+
+
+def _calc_N(Q, n):
+ """Return N as described by Equation 31"""
+ Q = normalised(Q)
+ n = normalised(n)
+ if is_small(angle_between_vectors(Q, n)):
+ # Replace the reference vector with an alternative vector from Eq.(78)
+ idx_min, _ = min(enumerate([abs(Q[0, 0]), abs(Q[1, 0]), abs(Q[2, 0])]), key=lambda v: v[1])
+ idx_1, idx_2 = [idx for idx in range(3) if idx != idx_min]
+ qval = sqrt(Q[idx_1, 0] * Q[idx_1, 0] + Q[idx_2, 0] * Q[idx_2, 0])
+ n[idx_min, 0] = qval
+ n[idx_1, 0] = -Q[idx_min, 0] * Q[idx_1, 0] / qval
+ n[idx_2, 0] = -Q[idx_min, 0] * Q[idx_2, 0] / qval
+ if is_small(norm(n)):
+ n[idx_min, 0] = 0
+ n[idx_1, 0] = Q[idx_2, 0] / qval
+ n[idx_2, 0] = -Q[idx_1, 0] / qval
+ Qxn = cross3(Q, n)
+ QxnxQ = cross3(Qxn, Q)
+ QxnxQ = normalised(QxnxQ)
+ Qxn = normalised(Qxn)
+ return matrix([[Q[0, 0], QxnxQ[0, 0], Qxn[0, 0]],
+ [Q[1, 0], QxnxQ[1, 0], Qxn[1, 0]],
+ [Q[2, 0], QxnxQ[2, 0], Qxn[2, 0]]])
+
+
+def _calc_angle_between_naz_and_qaz(theta, alpha, tau):
+ # Equation 30:
+ top = cos(tau) - sin(alpha) * sin(theta)
+ bottom = cos(alpha) * cos(theta)
+ if is_small(bottom):
+ if is_small(cos(alpha)):
+ raise ValueError('cos(alpha) is too small')
+ if is_small(cos(theta)):
+ raise ValueError('cos(theta) is too small')
+ if is_small(sin(tau)):
+ return 0.
+ return acos(bound(top / bottom))
+
+
+def youAnglesToHkl(pos, wavelength, UBmatrix):
+ """Calculate miller indices from position in radians.
+ """
+
+ [MU, DELTA, NU, ETA, CHI, PHI] = create_you_matrices(*pos.totuple())
+
+ q_lab = (NU * DELTA - I) * matrix([[0], [2 * pi / wavelength], [0]]) # 12
+
+ hkl = UBmatrix.I * PHI.I * CHI.I * ETA.I * MU.I * q_lab
+
+ return hkl[0, 0], hkl[1, 0], hkl[2, 0]
+
+
+def _tidy_degenerate_solutions(pos, constraints):
+
+ original = pos.inDegrees()
+ detector_like_constraint = constraints.detector or constraints.naz
+ nu_constrained_to_0 = is_small(pos.nu) and detector_like_constraint
+ mu_constrained_to_0 = is_small(pos.mu) and 'mu' in constraints.sample
+ delta_constrained_to_0 = is_small(pos.delta) and detector_like_constraint
+ eta_constrained_to_0 = is_small(pos.eta) and 'eta' in constraints.sample
+ phi_not_constrained = not 'phi' in constraints.sample
+
+ if nu_constrained_to_0 and mu_constrained_to_0 and phi_not_constrained:
+ # constrained to vertical 4-circle like mode
+ if is_small(pos.chi): # phi || eta
+ desired_eta = pos.delta / 2.
+ eta_diff = desired_eta - pos.eta
+ pos.eta = desired_eta
+ pos.phi -= eta_diff
+ if PRINT_DEGENERATE:
+ print ('DEGENERATE: with chi=0, phi and eta are colinear:'
+ 'choosing eta = delta/2 by adding % 7.3f to eta and '
+ 'removing it from phi. (mu=%s=0 only)' % (eta_diff * TODEG, NUNAME))
+ print ' original:', original
+
+ elif delta_constrained_to_0 and eta_constrained_to_0 and phi_not_constrained:
+ # constrained to horizontal 4-circle like mode
+ if is_small(pos.chi - pi / 2): # phi || mu
+ desired_mu = pos.nu / 2.
+ mu_diff = desired_mu - pos.mu
+ pos.mu = desired_mu
+ pos.phi += mu_diff
+ if PRINT_DEGENERATE:
+ print ('DEGENERATE: with chi=90, phi and mu are colinear: choosing'
+ ' mu = %s/2 by adding % 7.3f to mu and to phi. '
+ '(delta=eta=0 only)' % (NUNAME, mu_diff * TODEG))
+ print ' original:', original
+
+ return pos
+
+
+def _theta_and_qaz_from_detector_angles(delta, nu):
+ # Equation 19:
+ cos_2theta = cos(delta) * cos(nu)
+ theta = acos(cos_2theta) / 2.
+ sgn = sign(sin(2. * theta))
+ qaz = atan2(sgn * sin(delta), sgn * cos(delta) * sin(nu))
+ return theta, qaz
+
+
+class YouUbCalcStrategy(PaperSpecificUbCalcStrategy):
+
+ def calculate_q_phi(self, pos):
+
+ [MU, DELTA, NU, ETA, CHI, PHI] = create_you_matrices(*pos.totuple())
+ # Equation 12: Compute the momentum transfer vector in the lab frame
+ y = matrix('0; 1; 0')
+ q_lab = (NU * DELTA - I) * y
+ # Transform this into the phi frame.
+ return PHI.I * CHI.I * ETA.I * MU.I * q_lab
+
+
+UNREACHABLE_MSG = (
+ 'The current combination of constraints with %s = %.4f\n'
+ 'prohibits a solution for the specified reflection.')
+
+
+class YouHklCalculator(HklCalculatorBase):
+
+ def __init__(self, ubcalc, geometry, hardware, constraints,
+ raiseExceptionsIfAnglesDoNotMapBackToHkl=True):
+ HklCalculatorBase.__init__(self, ubcalc, geometry, hardware,
+ raiseExceptionsIfAnglesDoNotMapBackToHkl)
+ self._hardware = hardware # for checking limits only
+ self.constraints = constraints
+ self.parameter_manager = constraints # TODO: remove need for this attr
+
+ def __str__(self):
+ return self.constraints.__str__()
+
+ def _get_n_phi(self):
+ return self._ubcalc.n_phi
+
+ def _get_ubmatrix(self):
+ return self._getUBMatrix() # for consistency
+
+ def repr_mode(self):
+ return repr(self.constraints.all)
+
+ def _anglesToHkl(self, pos, wavelength):
+ """Calculate miller indices from position in radians.
+ """
+ return youAnglesToHkl(pos, wavelength, self._get_ubmatrix())
+
+ def _anglesToVirtualAngles(self, pos, _wavelength):
+ """Calculate pseudo-angles in radians from position in radians.
+
+ Return theta, qaz, alpha, naz, tau, psi and beta in a dictionary.
+
+ """
+
+ # depends on surface normal n_lab.
+ mu, delta, nu, eta, chi, phi = pos.totuple()
+
+ theta, qaz = _theta_and_qaz_from_detector_angles(delta, nu) # (19)
+
+ [MU, _, _, ETA, CHI, PHI] = create_you_matrices(mu,
+ delta, nu, eta, chi, phi)
+ Z = MU * ETA * CHI * PHI
+ n_lab = Z * self._get_n_phi()
+ alpha = asin(bound((-n_lab[1, 0])))
+ naz = atan2(n_lab[0, 0], n_lab[2, 0]) # (20)
+
+ cos_tau = cos(alpha) * cos(theta) * cos(naz - qaz) + \
+ sin(alpha) * sin(theta)
+ tau = acos(bound(cos_tau)) # (23)
+
+ # Compute Tau using the dot product directly (THIS ALSO WORKS)
+# q_lab = ( (NU * DELTA - I ) * matrix([[0],[1],[0]])
+# norm = norm(q_lab)
+# q_lab = matrix([[1],[0],[0]]) if norm == 0 else q_lab * (1/norm)
+# tau_from_dot_product = acos(bound(dot3(q_lab, n_lab)))
+
+ sin_beta = 2 * sin(theta) * cos(tau) - sin(alpha)
+ beta = asin(bound(sin_beta)) # (24)
+
+ psi = next(self._calc_psi(alpha, theta, tau, qaz, naz))
+
+ return {'theta': theta, 'qaz': qaz, 'alpha': alpha,
+ 'naz': naz, 'tau': tau, 'psi': psi, 'beta': beta}
+
+
+ def _choose_single_solution(self, pos_virtual_angles_pairs_in_degrees):
+
+ if len(pos_virtual_angles_pairs_in_degrees) == 1:
+ return pos_virtual_angles_pairs_in_degrees[0]
+
+ absolute_distances = []
+ for pos_, _ in pos_virtual_angles_pairs_in_degrees:
+ absolute_distances.append(sum([abs(pos_.totuple()[i]) for i in (0, 3, 4, 5)]))
+
+ shortest_solution_index = absolute_distances.index(
+ min(absolute_distances))
+ pos, virtual_angles = pos_virtual_angles_pairs_in_degrees[shortest_solution_index]
+
+ if logger.isEnabledFor(logging.INFO):
+ msg = ('Multiple sample solutions found (choosing solution with '
+ 'shortest distance to all-zeros position):\n')
+ i = 0
+ for (pos_, _), distance in zip(pos_virtual_angles_pairs_in_degrees,
+ absolute_distances):
+ msg += '*' if i == shortest_solution_index else '.'
+
+ msg += ('mu=% 7.3f, delta=% 7.3f, nu=% 7.3f, eta=% 7.3f, chi=% 7.3f, phi=% 7.3f' %
+ pos_.totuple())
+ msg += ' (distance=% 4.3f)\n' % (distance * TODEG)
+ i += 1
+ msg += ':\n'
+ logger.info(msg)
+
+ return pos, virtual_angles
+
+ def hklToAngles(self, h, k, l, wavelength, return_all_solutions=False):
+ """
+ Return verified Position and all virtual angles in degrees from
+ h, k & l and wavelength in Angstroms.
+
+ The calculated Position is verified by checking that it maps back using
+ anglesToHkl() to the requested hkl value.
+
+ Those virtual angles fixed or generated while calculating the position
+ are verified by by checking that they map back using
+ anglesToVirtualAngles to the virtual angles for the given position.
+
+ Throws a DiffcalcException if either check fails and
+ raiseExceptionsIfAnglesDoNotMapBackToHkl is True, otherwise displays a
+ warning.
+ """
+
+ pos_virtual_angles_pairs = self._hklToAngles(h, k, l, wavelength, return_all_solutions) # in rad
+ assert pos_virtual_angles_pairs
+ pos_virtual_angles_pairs_in_degrees = []
+ for pos, virtual_angles in pos_virtual_angles_pairs:
+
+ # to degrees:
+ pos.changeToDegrees()
+ for key, val in virtual_angles.items():
+ if val is not None:
+ virtual_angles[key] = val * TODEG
+
+ self._verify_pos_map_to_hkl(h, k, l, wavelength, pos)
+
+ pos_virtual_angles_pairs_in_degrees.append((pos, virtual_angles))
+
+ if return_all_solutions:
+ return pos_virtual_angles_pairs_in_degrees
+ else:
+ pos, virtual_angles = self._choose_single_solution(pos_virtual_angles_pairs_in_degrees)
+ return pos, virtual_angles
+
+
+ def hkl_to_all_angles(self, h, k, l, wavelength):
+ return self.hklToAngles(h, k, l, wavelength, True)
+
+
+ def _hklToAngles(self, h, k, l, wavelength, return_all_solutions=False):
+ """(pos, virtualAngles) = hklToAngles(h, k, l, wavelength) --- with
+ Position object pos and the virtual angles returned in degrees. Some
+ modes may not calculate all virtual angles.
+ """
+
+ if not self.constraints.is_fully_constrained():
+ raise DiffcalcException(
+ "Diffcalc is not fully constrained.\n"
+ "Type 'help con' for instructions")
+
+ if not self.constraints.is_current_mode_implemented():
+ raise DiffcalcException(
+ "Sorry, the selected constraint combination is valid but "
+ "is not implemented. Type 'help con' for implemented combinations")
+
+ # constraints are dictionaries
+ ref_constraint = self.constraints.reference
+ if ref_constraint:
+ ref_constraint_name, ref_constraint_value = ref_constraint.items()[0]
+ det_constraint = self.constraints.detector
+ naz_constraint = self.constraints.naz
+ samp_constraints = self.constraints.sample
+
+ assert not (det_constraint and naz_constraint), (
+ "Two 'detector' constraints given")
+
+
+ h_phi = self._get_ubmatrix() * matrix([[h], [k], [l]])
+ theta = self._calc_theta(h_phi, wavelength)
+ tau = angle_between_vectors(h_phi, self._get_n_phi())
+
+ if is_small(sin(tau)) and ref_constraint:
+ if ref_constraint_name == 'psi':
+ raise DiffcalcException("Azimuthal angle 'psi' is undefined as reference and scattering vectors parallel.\n"
+ "Please constrain one of the sample angles or choose different reference vector orientation.")
+ elif ref_constraint_name == 'a_eq_b':
+ raise DiffcalcException("Reference constraint 'a_eq_b' is redundant as reference and scattering vectors are parallel.\n"
+ "Please constrain one of the sample angles or choose different reference vector orientation.")
+
+ ### Reference constraint column ###
+
+ if ref_constraint:
+ # An angle for the reference vector (n) is given (Section 5.2)
+ alpha, _ = self._calc_remaining_reference_angles(
+ ref_constraint_name, ref_constraint_value, theta, tau)
+
+ solution_tuples = []
+ if det_constraint or naz_constraint:
+
+ if len(samp_constraints) == 1:
+ for qaz, naz, delta, nu in self._calc_det_angles_given_det_or_naz_constraint(
+ det_constraint, naz_constraint, theta, tau, alpha):
+ for mu, eta, chi, phi in self._calc_sample_angles_from_one_sample_constraint(
+ samp_constraints, h_phi, theta, alpha, qaz, naz):
+ solution_tuples.append((mu, delta, nu, eta, chi, phi))
+
+ elif len(samp_constraints) == 2:
+ if det_constraint:
+ det_constraint_name, det_constraint_val = det_constraint.items()[0]
+ for delta, nu, qaz in self._calc_remaining_detector_angles(det_constraint_name, det_constraint_val, theta):
+ for mu, eta, chi, phi in self._calc_sample_angles_given_two_sample_and_detector(
+ samp_constraints, qaz, theta, h_phi, self._get_n_phi()):
+ solution_tuples.append((mu, delta, nu, eta, chi, phi))
+
+ else:
+ raise DiffcalcException(
+ 'No code yet to handle this combination of detector and sample constraints!')
+
+ elif len(samp_constraints) == 2:
+ if ref_constraint_name == 'psi':
+ psi_vals = [ref_constraint_value,]
+ else:
+ psi_vals = self._calc_psi(alpha, theta, tau)
+ for psi in psi_vals:
+ angles = list(self._calc_sample_given_two_sample_and_reference(
+ samp_constraints, h_phi, theta, psi))
+ solution_tuples.extend(angles)
+
+ elif len(samp_constraints) == 3:
+ for angles in self._calc_angles_given_three_sample_constraints(
+ h, k, l, wavelength, return_all_solutions, samp_constraints,
+ h_phi, theta):
+ solution_tuples.append(angles)
+
+ if not solution_tuples:
+ raise DiffcalcException('No solutions were found. '
+ 'Please consider using an alternative set of constraints.')
+
+ tidy_solutions = [_tidy_degenerate_solutions(YouPosition(*pos, unit='RAD'),
+ self.constraints).totuple() for pos in solution_tuples]
+ merged_solution_tuples = set(self._filter_angle_limits(tidy_solutions,
+ not return_all_solutions))
+ if not merged_solution_tuples:
+ raise DiffcalcException('No solutions were found matching existing hardware limits. '
+ 'Please consider using an alternative set of constraints.')
+
+ #def _find_duplicate_angles(el):
+ # idx, tpl = el
+ # for tmp_tpl in filtered_solutions[idx:]:
+ # if False not in [abs(x-y) < SMALL for x,y in zip(tmp_tpl, tpl)]:
+ # return False
+ # return True
+ #merged_solution_tuples = filter(_find_duplicate_angles, enumerate(filtered_solutions, 1))
+ position_pseudo_angles_pairs = self._create_position_pseudo_angles_pairs(wavelength, merged_solution_tuples)
+ if not position_pseudo_angles_pairs:
+ raise DiffcalcException('No solutions were found. Please check hardware limits and '
+ 'consider using an alternative pseudo-angle constraints.')
+
+ return position_pseudo_angles_pairs
+
+
+ def _create_position_pseudo_angles_pairs(self, wavelength, merged_solution_tuples):
+
+ position_pseudo_angles_pairs = []
+ for pos in merged_solution_tuples:
+ # Create position
+ position = YouPosition(*pos, unit='RAD')
+ #position = _tidy_degenerate_solutions(position, self.constraints)
+ #if position.phi <= -pi + SMALL:
+ # position.phi += 2 * pi
+ # pseudo angles calculated along the way were for the initial solution
+ # and may be invalid for the chosen solution TODO: anglesToHkl need no
+ # longer check the pseudo_angles as they will be generated with the
+ # same function and it will prove nothing
+ pseudo_angles = self._anglesToVirtualAngles(position, wavelength)
+ is_sol = True
+ for constraint in [self.constraints.reference,
+ self.constraints.detector,
+ self.constraints.naz]:
+ try:
+ constraint_name, constraint_value = constraint.items()[0]
+ if constraint_name == 'a_eq_b':
+ if not is_small(pseudo_angles['alpha'] - pseudo_angles['beta']):
+ is_sol = False
+ break
+ else:
+ if not is_small(constraint_value - pseudo_angles[constraint_name]):
+ is_sol = False
+ break
+ except:
+ continue
+ if is_sol:
+ position_pseudo_angles_pairs.append((position, pseudo_angles))
+ return position_pseudo_angles_pairs
+
+
+ def _calc_theta(self, h_phi, wavelength):
+ """Calculate theta using Equation1
+ """
+ q_length = norm(h_phi)
+ if is_small(q_length):
+ raise DiffcalcException('Reflection is unreachable as |Q| is too small')
+ wavevector = 2 * pi / wavelength
+ try:
+ theta = asin(bound(q_length / (2 * wavevector)))
+ except AssertionError:
+ raise DiffcalcException(
+ 'Reflection is unreachable as |Q| is too long')
+ if is_small(cos(theta)):
+ raise DiffcalcException(
+ 'Reflection is unreachable as theta angle is too close to 90 deg')
+ return theta
+
+ def _calc_psi(self, alpha, theta, tau, qaz=None, naz=None):
+ """Calculate psi from Eq. (18), (25) and (28)
+ """
+ sin_tau = sin(tau)
+ cos_theta = cos(theta)
+ if is_small(sin_tau):
+ # The reference vector is parallel to the scattering vector
+ yield float('nan')
+ elif is_small(cos_theta):
+ # Reflection is unreachable as theta angle is too close to 90 deg
+ yield float('nan')
+ elif is_small(sin(theta)):
+ # Reflection is unreachable as |Q| is too small
+ yield float('nan')
+ else:
+ cos_psi = ((cos(tau) * sin(theta) - sin(alpha)) / cos_theta) # (28)
+ if qaz is None or naz is None :
+ try:
+ acos_psi = acos(bound(cos_psi / sin_tau))
+ if is_small(acos_psi):
+ yield 0.
+ else:
+ for psi in [acos_psi, -acos_psi]:
+ yield psi
+ except AssertionError:
+ print ('WARNING: Diffcalc could not calculate an azimuth (psi)')
+ yield float('nan')
+ else:
+ sin_psi = cos(alpha) * sin(qaz - naz)
+ sgn = sign(sin_tau)
+ eps = sin_psi**2 + cos_psi**2
+ sigma_ = eps/sin_tau**2 - 1
+ if not is_small(sigma_):
+ print ('WARNING: Diffcalc could not calculate a unique azimuth '
+ '(psi) because of loss of accuracy in numerical calculation')
+ yield float('nan')
+ else:
+ psi = atan2(sgn * sin_psi, sgn * cos_psi)
+ yield psi
+
+
+ def _calc_remaining_reference_angles(self, name, value, theta, tau):
+ """Return alpha and beta given one of a_eq_b, alpha, beta or psi
+ """
+ if name == 'psi':
+ psi = value
+ # Equation 26 for alpha
+ sin_alpha = (cos(tau) * sin(theta) -
+ cos(theta) * sin(tau) * cos(psi))
+ if abs(sin_alpha) > 1 + SMALL:
+ raise DiffcalcException(UNREACHABLE_MSG % (name, value * TODEG))
+ alpha = asin(bound(sin_alpha))
+ # Equation 27 for beta
+ sin_beta = cos(tau) * sin(theta) + cos(theta) * sin(tau) * cos(psi)
+ if abs(sin_beta) > 1 + SMALL:
+ raise DiffcalcException(UNREACHABLE_MSG % (name, value * TODEG))
+
+ beta = asin(bound(sin_beta))
+
+ elif name == 'a_eq_b':
+ alpha = beta = asin(cos(tau) * sin(theta)) # (24)
+
+ elif name == 'alpha':
+ alpha = value # (24)
+ sin_beta = 2 * sin(theta) * cos(tau) - sin(alpha)
+ if abs(sin_beta) > 1 + SMALL:
+ raise DiffcalcException(UNREACHABLE_MSG % (name, value * TODEG))
+ beta = asin(sin_beta)
+
+ elif name == 'beta':
+ beta = value
+ sin_alpha = 2 * sin(theta) * cos(tau) - sin(beta) # (24)
+ if abs(sin_alpha) > 1 + SMALL:
+ raise DiffcalcException(UNREACHABLE_MSG % (name, value * TODEG))
+
+ alpha = asin(sin_alpha)
+
+ return alpha, beta
+
+ def _calc_det_angles_given_det_or_naz_constraint(
+ self, det_constraint, naz_constraint, theta, tau, alpha):
+
+ assert det_constraint or naz_constraint
+ try:
+ naz_qaz_angle = _calc_angle_between_naz_and_qaz(theta, alpha, tau)
+ except AssertionError:
+ return
+ if det_constraint:
+ # One of the detector angles is given (Section 5.1)
+ det_constraint_name, det_constraint = det_constraint.items()[0]
+ for delta, nu, qaz in self._calc_remaining_detector_angles(
+ det_constraint_name, det_constraint, theta):
+ if is_small(naz_qaz_angle):
+ naz_angles = [qaz,]
+ else:
+ naz_angles = [qaz - naz_qaz_angle, qaz + naz_qaz_angle]
+ for naz in naz_angles:
+ yield qaz, naz, delta, nu
+ elif naz_constraint: # The 'detector' angle naz is given:
+ det_constraint_name, det_constraint = naz_constraint.items()[0]
+ naz_name, naz = det_constraint_name, det_constraint
+ assert naz_name == 'naz'
+ if is_small(naz_qaz_angle):
+ qaz_angles = [naz,]
+ else:
+ qaz_angles = [naz - naz_qaz_angle, naz + naz_qaz_angle]
+ for qaz in qaz_angles:
+ for delta, nu, _ in self._calc_remaining_detector_angles(
+ 'qaz', qaz, theta):
+ yield qaz, naz, delta, nu
+
+ def _calc_remaining_detector_angles(self, constraint_name,
+ constraint_value, theta):
+ """Return delta, nu and qaz given one detector angle
+ """
+ # (section 5.1)
+ # Find qaz using various derivations of 17 and 18
+ sin_2theta = sin(2 * theta)
+ cos_2theta = cos(2 * theta)
+ if is_small(sin_2theta):
+ raise DiffcalcException(
+ 'No meaningful scattering vector (Q) can be found when '
+ 'theta is so small (%.4f).' % theta * TODEG)
+
+ if constraint_name == 'delta':
+ delta = constraint_value
+ try:
+ asin_qaz = asin(bound(sin(delta) / sin_2theta)) # (17 & 18)
+ except AssertionError:
+ return
+ cos_delta = cos(delta)
+ if is_small(cos_delta):
+ #raise DiffcalcException(
+ # 'The %s and %s circles are redundant when delta is constrained to %.0f degrees.'
+ # 'Please change delta constraint or use 4-circle mode.' % (NUNAME, 'mu', delta * TODEG))
+ print (('DEGENERATE: with delta=90, %s is degenerate: choosing '
+ '%s = 0 (allowed because %s is unconstrained)') %
+ (NUNAME, NUNAME, NUNAME))
+ acos_nu = 1.
+ else:
+ try:
+ acos_nu = acos(bound(cos_2theta / cos_delta))
+ except AssertionError:
+ return
+ if is_small(cos(asin_qaz)):
+ qaz_angles = [sign(asin_qaz) * pi / 2.,]
+ else:
+ qaz_angles = [asin_qaz, pi - asin_qaz]
+ if is_small(acos_nu):
+ nu_angles = [0.,]
+ else:
+ nu_angles = [acos_nu, -acos_nu]
+ for qaz, nu in product(qaz_angles, nu_angles):
+ sgn_ref = sign(sin_2theta) * sign(cos(qaz))
+ sgn_ratio = sign(sin(nu)) * sign(cos_delta)
+ if sgn_ref == sgn_ratio:
+ yield delta, nu, qaz
+
+ elif constraint_name == NUNAME:
+ nu = constraint_value
+ cos_nu = cos(nu)
+ if is_small(cos_nu):
+ raise DiffcalcException(
+ 'The %s circle constraint to %.0f degrees is redundant.'
+ 'Please change this constraint or use 4-circle mode.' % (NUNAME, nu * TODEG))
+ cos_delta = cos_2theta / cos(nu)
+ cos_qaz = cos_delta * sin(nu) / sin_2theta
+ try:
+ acos_delta = acos(bound(cos_delta))
+ acos_qaz = acos(bound(cos_qaz))
+ except AssertionError:
+ return
+ if is_small(acos_qaz):
+ qaz_angles = [0.,]
+ else:
+ qaz_angles = [acos_qaz, -acos_qaz]
+ if is_small(acos_delta):
+ delta_angles = [0.,]
+ else:
+ delta_angles = [acos_delta, -acos_delta]
+ for qaz, delta in product(qaz_angles, delta_angles):
+ sgn_ref = sign(sin(delta))
+ sgn_ratio = sign(sin(qaz)) * sign(sin_2theta)
+ if sgn_ref == sgn_ratio:
+ yield delta, nu, qaz
+
+ elif constraint_name == 'qaz':
+ qaz = constraint_value
+ asin_delta = asin(sin(qaz) * sin_2theta)
+ if is_small(cos(asin_delta)):
+ delta_angles = [sign(asin_delta) * pi / 2.,]
+ else:
+ delta_angles = [asin_delta, pi - asin_delta]
+ for delta in delta_angles:
+ cos_delta = cos(delta)
+ if is_small(cos_delta):
+ print (('DEGENERATE: with delta=90, %s is degenerate: choosing '
+ '%s = 0 (allowed because %s is unconstrained)') %
+ (NUNAME, NUNAME, NUNAME))
+ #raise DiffcalcException(
+ # 'The %s circle is redundant when delta is at %.0f degrees.'
+ # 'Please change detector constraint or use 4-circle mode.' % (NUNAME, delta * TODEG))
+ nu = 0.
+ else:
+ sgn_delta = sign(cos_delta)
+ nu = atan2(sgn_delta * sin_2theta * cos(qaz), sgn_delta * cos_2theta)
+ yield delta, nu, qaz
+ else:
+ raise DiffcalcException(
+ constraint_name + ' is not an explicit detector angle '
+ '(naz cannot be handled here)')
+
+
+ def _calc_sample_angles_from_one_sample_constraint(
+ self, samp_constraints, h_phi, theta, alpha, qaz, naz):
+
+ sample_constraint_name, sample_value = samp_constraints.items()[0]
+ q_lab = matrix([[cos(theta) * sin(qaz)],
+ [-sin(theta)],
+ [cos(theta) * cos(qaz)]]) # (18)
+ n_lab = matrix([[cos(alpha) * sin(naz)],
+ [-sin(alpha)],
+ [cos(alpha) * cos(naz)]]) # (20)
+ mu_eta_chi_phi_tuples = list(self._calc_remaining_sample_angles(
+ sample_constraint_name, sample_value, q_lab, n_lab, h_phi,
+ self._get_n_phi()))
+ return mu_eta_chi_phi_tuples
+
+ def _calc_sample_given_two_sample_and_reference(
+ self, samp_constraints, h_phi, theta, psi):
+
+ for angles in self._calc_sample_angles_given_two_sample_and_reference(
+ samp_constraints, psi, theta, h_phi, self._get_n_phi()):
+ qaz, psi, mu, eta, chi, phi = angles
+ values_in_deg = tuple(v * TODEG for v in angles)
+ logger.info('Initial angles: xi=%.3f, psi=%.3f, mu=%.3f, '
+ 'eta=%.3f, chi=%.3f, phi=%.3f' %
+ values_in_deg) # Try to find a solution for each possible transformed xi
+
+ logger.info("")
+ msg = "---Trying psi=%.3f, qaz=%.3f" % (psi * TODEG, qaz * TODEG)
+ logger.info(msg)
+
+ for delta, nu, _ in self._calc_remaining_detector_angles('qaz', qaz, theta):
+ logger.info("delta=%.3f, %s=%.3f", delta * TODEG, NUNAME, nu * TODEG)
+ #for mu, eta, chi, phi in self._generate_sample_solutions(
+ # mu, eta, chi, phi, samp_constraints.keys(), delta,
+ # nu, wavelength, (h, k, l), ref_constraint_name,
+ # ref_constraint_value):
+ yield mu, delta, nu, eta, chi, phi
+
+ def _calc_remaining_sample_angles(self, constraint_name, constraint_value,
+ q_lab, n_lab, q_phi, n_phi):
+ """Return phi, chi, eta and mu, given one of these"""
+ # (section 5.3)
+
+ N_lab = _calc_N(q_lab, n_lab)
+ N_phi = _calc_N(q_phi, n_phi)
+ Z = N_lab * N_phi.T
+
+ if constraint_name == 'mu': # (35)
+ mu = constraint_value
+ V = calcMU(mu).I * N_lab * N_phi.T
+ try:
+ acos_chi = acos(bound(V[2, 2]))
+ except AssertionError:
+ return
+ if is_small(sin(acos_chi)):
+ # chi ~= 0 or 180 and therefor phi || eta The solutions for phi
+ # and eta here will be valid but will be chosen unpredictably.
+ # Choose eta=0:
+ #
+ # tan(phi+eta)=v12/v11 from docs/extensions_to_yous_paper.wxm
+ chi = acos_chi
+ eta = 0.
+ phi = atan2(-V[1, 0], V[1, 1])
+ logger.debug(
+ 'Eta and phi cannot be chosen uniquely with chi so close '
+ 'to 0 or 180. Returning phi=%.3f and eta=%.3f',
+ phi * TODEG, eta * TODEG)
+ yield mu, eta, chi, phi
+ else:
+ for chi in [acos_chi, -acos_chi]:
+ sgn = sign(sin(chi))
+ phi = atan2(-sgn * V[2, 1], -sgn * V[2, 0])
+ eta = atan2(-sgn * V[1, 2], sgn * V[0, 2])
+ yield mu, eta, chi, phi
+
+ elif constraint_name == 'phi': # (37)
+ phi = constraint_value
+ V = N_lab * N_phi.I * calcPHI(phi).T
+ try:
+ asin_eta = asin(bound(V[0, 1]))
+ except AssertionError:
+ return
+ if is_small(cos(asin_eta)):
+ raise DiffcalcException('Chi and mu cannot be chosen uniquely '
+ 'with eta so close to +/-90.')
+ for eta in [asin_eta, pi - asin_eta]:
+ sgn = sign(cos(eta))
+ mu = atan2(sgn * V[2, 1], sgn * V[1, 1])
+ chi = atan2(sgn * V[0, 2], sgn * V[0, 0])
+ yield mu, eta, chi, phi
+
+ elif constraint_name in ('eta', 'chi'):
+ if constraint_name == 'eta': # (39)
+ eta = constraint_value
+ cos_eta = cos(eta)
+ if is_small(cos_eta):
+ #TODO: Not likely to happen in real world!?
+ raise DiffcalcException(
+ 'Chi and mu cannot be chosen uniquely with eta '
+ 'constrained so close to +-90.')
+ try:
+ asin_chi = asin(bound(Z[0, 2] / cos_eta))
+ except AssertionError:
+ return
+ all_eta = [eta,]
+ all_chi = [asin_chi, pi - asin_chi]
+
+ else: # constraint_name == 'chi' # (40)
+ chi = constraint_value
+ sin_chi = sin(chi)
+ if is_small(sin_chi):
+ raise DiffcalcException(
+ 'Eta and phi cannot be chosen uniquely with chi '
+ 'constrained so close to 0. (Please contact developer '
+ 'if this case is useful for you)')
+ try:
+ acos_eta = acos(bound(Z[0, 2] / sin_chi))
+ except AssertionError:
+ return
+ all_eta = [acos_eta, -acos_eta]
+ all_chi = [chi,]
+
+ for chi, eta in product(all_chi, all_eta):
+ top_for_mu = Z[2, 2] * sin(eta) * sin(chi) + Z[1, 2] * cos(chi)
+ bot_for_mu = -Z[2, 2] * cos(chi) + Z[1, 2] * sin(eta) * sin(chi)
+ if is_small(top_for_mu) and is_small(bot_for_mu):
+ # chi == +-90, eta == 0/180 and therefore phi || mu cos(chi) ==
+ # 0 and sin(eta) == 0 Experience shows that even though e.g.
+ # the z[2, 2] and z[1, 2] values used to calculate mu may be
+ # basically 0 (1e-34) their ratio in every case tested so far
+ # still remains valid and using them will result in a phi
+ # solution that is continuous with neighbouring positions.
+ #
+ # We cannot test phi minus mu here unfortunately as the final
+ # phi and mu solutions have not yet been chosen (they may be
+ # +-x or 180+-x). Otherwise we could choose a sensible solution
+ # here if the one found was incorrect.
+
+ # tan(phi+eta)=v12/v11 from extensions_to_yous_paper.wxm
+ phi_minus_mu = -atan2(Z[2, 0], Z[1, 1])
+ logger.debug(
+ 'Mu and phi cannot be chosen uniquely with chi so close '
+ 'to +/-90 and eta so close 0 or 180.\n After the final '
+ 'solution has been chose phi-mu should equal: %.3f',
+ phi_minus_mu * TODEG)
+ mu = atan2(-top_for_mu, -bot_for_mu) # (41)
+
+ top_for_phi = Z[0, 1] * cos(eta) * cos(chi) - Z[0, 0] * sin(eta)
+ bot_for_phi = Z[0, 1] * sin(eta) + Z[0, 0] * cos(eta) * cos(chi)
+ phi = atan2(top_for_phi, bot_for_phi) # (42)
+ # if is_small(bot_for_phi) and is_small(top_for_phi):
+ # raise DiffcalcException(
+ # 'phi=%.3f cannot be known with confidence as top and '
+ # 'bottom are both close to zero. chi=%.3f, eta=%.3f'
+ # % (mu * TODEG, chi * TODEG, eta * TODEG))
+ yield mu, eta, chi, phi
+
+ else:
+ raise DiffcalcException('Given angle must be one of phi, chi, eta or mu')
+
+ def _calc_angles_given_three_sample_constraints(
+ self, h, k, l, wavelength, return_all_solutions, samp_constraints,
+ h_phi, theta):
+
+ if not 'mu' in samp_constraints:
+ eta_ = self.constraints.sample['eta']
+ chi_ = self.constraints.sample['chi']
+ phi_ = self.constraints.sample['phi']
+ try:
+ two_mu_qaz_pairs = _mu_and_qaz_from_eta_chi_phi(eta_, chi_, phi_, theta, h_phi)
+ except AssertionError:
+ return
+ else:
+ raise DiffcalcException(
+ 'No code yet to handle this combination of 3 sample constraints!')
+ # TODO: Code duplicated above
+ for mu_, qaz in two_mu_qaz_pairs:
+ logger.debug("--- Trying mu_:%.f qaz_%.f", mu_ * TODEG, qaz * TODEG)
+ for delta, nu, _ in self._calc_remaining_detector_angles('qaz', qaz, theta):
+ logger.info("delta=%.3f, %s=%.3f", delta * TODEG, NUNAME, nu * TODEG)
+ yield mu_, delta, nu, eta_, chi_, phi_
+
+ def _calc_sample_angles_given_two_sample_and_reference(
+ self, samp_constraints, psi, theta, q_phi, n_phi):
+ """Available combinations:
+ chi, phi, reference
+ mu, eta, reference,
+ chi, eta, reference
+ chi, mu, reference
+ """
+
+ def __get_phi_and_qaz(chi, eta, mu):
+ a = sin(chi) * cos(eta)
+ b = sin(chi) * sin(eta) * sin(mu) - cos(chi) * cos(mu)
+ #atan2_xi = atan2(V[2, 2] * a + V[2, 0] * b,
+ # V[2, 0] * a - V[2, 2] * b) # (54)
+ qaz = atan2(V[2, 0] * a - V[2, 2] * b,
+ -V[2, 2] * a - V[2, 0] * b) # (54)
+
+ a = sin(chi) * sin(mu) - cos(mu) * cos(chi) * sin(eta)
+ b = cos(mu) * cos(eta)
+ phi = atan2(V[1, 1] * a - V[0, 1] * b,
+ V[0, 1] * a + V[1, 1] * b) # (55)
+ # if is_small(mu+pi/2) and is_small(eta) and False:
+ # phi_general = phi
+ # # solved in extensions_to_yous_paper.wxm
+ # phi = atan2(V[1, 1], V[0, 1])
+ # logger.info("phi = %.3f or %.3f (std)",
+ # phi*TODEG, phi_general*TODEG )
+
+ return qaz, phi
+
+ N_phi = _calc_N(q_phi, n_phi)
+ THETA = z_rotation(-theta)
+ PSI = x_rotation(psi)
+
+ if 'chi' in samp_constraints and 'phi' in samp_constraints:
+
+ chi = samp_constraints['chi']
+ phi = samp_constraints['phi']
+
+ CHI = calcCHI(chi)
+ PHI = calcPHI(phi)
+ V = CHI * PHI * N_phi * PSI.T * THETA.T # (46)
+
+ #atan2_xi = atan2(-V[2, 0], V[2, 2])
+ #atan2_eta = atan2(-V[0, 1], V[1, 1])
+ #atan2_mu = atan2(-V[2, 1], sqrt(V[2, 2] ** 2 + V[2, 0] ** 2))
+ try:
+ asin_mu = asin(bound(-V[2, 1]))
+ except AssertionError:
+ return
+ for mu in [asin_mu, pi - asin_mu]:
+ sgn_cosmu = sign(cos(mu))
+ #xi = atan2(-sgn_cosmu * V[2, 0], sgn_cosmu * V[2, 2])
+ qaz = atan2(sgn_cosmu * V[2, 2], sgn_cosmu * V[2, 0], )
+ eta = atan2(-sgn_cosmu * V[0, 1], sgn_cosmu * V[1, 1])
+ yield qaz, psi, mu, eta, chi, phi
+
+ elif 'mu' in samp_constraints and 'eta' in samp_constraints:
+
+ mu = samp_constraints['mu']
+ eta = samp_constraints['eta']
+
+ V = N_phi * PSI.T * THETA.T # (49)
+ try:
+ bot = bound(-V[2, 1] / sqrt(sin(eta) ** 2 * cos(mu) ** 2 + sin(mu) ** 2))
+ except AssertionError:
+ return
+ if is_small(cos(mu) * sin(eta)):
+ eps = atan2(sin(eta) * cos(mu), sin(mu))
+ chi_vals = [eps + acos(bot), eps - acos(bot)]
+ else:
+ eps = atan2(sin(mu), sin(eta) * cos(mu))
+ chi_vals = [asin(bot) - eps, pi - asin(bot) - eps] # (52)
+
+ ## Choose final chi solution here to obtain compatable xi and mu
+ ## TODO: This temporary solution works only for one case used on i07
+ ## Return a list of possible solutions?
+ #if is_small(eta) and is_small(mu + pi / 2):
+ # for chi in _generate_transformed_values(chi_orig):
+ # if pi / 2 <= chi < pi:
+ # break
+ #else:
+ # chi = chi_orig
+
+ for chi in chi_vals:
+ qaz, phi = __get_phi_and_qaz(chi, eta, mu)
+ yield qaz, psi, mu, eta, chi, phi
+
+ elif 'chi' in samp_constraints and 'eta' in samp_constraints:
+
+ chi = samp_constraints['chi']
+ eta = samp_constraints['eta']
+
+ V = N_phi * PSI.T * THETA.T # (49)
+ try:
+ bot = bound(-V[2, 1] / sqrt(sin(eta) ** 2 * sin(chi) ** 2 + cos(chi) ** 2))
+ except AssertionError:
+ return
+ if is_small(cos(chi)):
+ eps = atan2(cos(chi), sin(chi) * sin(eta))
+ mu_vals = [eps + acos(bot), eps - acos(bot)]
+ else:
+ eps = atan2(sin(chi) * sin(eta), cos(chi))
+ mu_vals = [asin(bot) - eps, pi - asin(bot) - eps] # (52)
+
+ for mu in mu_vals:
+ qaz, phi = __get_phi_and_qaz(chi, eta, mu)
+ yield qaz, psi, mu, eta, chi, phi
+
+ elif 'chi' in samp_constraints and 'mu' in samp_constraints:
+
+ chi = samp_constraints['chi']
+ mu = samp_constraints['mu']
+
+ V = N_phi * PSI.T * THETA.T # (49)
+
+ try:
+ asin_eta = asin(bound((-V[2, 1] - cos(chi) * sin(mu)) / (sin(chi) * cos(mu))))
+ except AssertionError:
+ return
+
+ for eta in [asin_eta, pi - asin_eta]:
+ qaz, phi = __get_phi_and_qaz(chi, eta, mu)
+ yield qaz, psi, mu, eta, chi, phi
+
+ else:
+ raise DiffcalcException(
+ 'No code yet to handle this combination of 2 sample '
+ 'constraints and one reference!:' + str(samp_constraints))
+
+ def _calc_sample_angles_given_two_sample_and_detector(
+ self, samp_constraints, qaz, theta, q_phi, n_phi):
+ """Available combinations:
+ chi, phi, detector
+ mu, eta, detector
+ mu, phi, detector
+ """
+
+ N_phi = _calc_N(q_phi, n_phi)
+
+ if 'mu' in samp_constraints and 'eta' in samp_constraints:
+
+ mu = samp_constraints['mu']
+ eta = samp_constraints['eta']
+
+ F = y_rotation(qaz - pi/2.)
+ THETA = z_rotation(-theta)
+ V = calcETA(eta).T * calcMU(mu).T * F * THETA # (56)
+
+ try:
+ bot = bound(-V[1, 0] / sqrt(N_phi[0, 0]**2 + N_phi[1, 0]**2))
+ eps = atan2(N_phi[1, 0], N_phi[0, 0])
+ phi_vals = [asin(bot) + eps, pi - asin(bot) + eps] # (59)
+ except (AssertionError, ZeroDivisionError):
+ # For the case of (00l) reflection, where N_phi[0,0] = N_phi[1,0] = 0
+ chi = atan2(V[0, 0] * N_phi[2, 0], V[2, 0] * N_phi[2, 0]) # (57)
+ sgn_denom = sign(N_phi[1, 1] * N_phi[0, 2] - N_phi[1, 2] * N_phi[0, 1])
+ sin_phi = V[1, 1] * N_phi[1, 2] - V[1, 2] * N_phi[1, 1]
+ cos_phi = V[1, 1] * N_phi[0, 2] - V[1, 2] * N_phi[0, 1]
+ phi = atan2(sin_phi * sgn_denom, cos_phi * sgn_denom)
+ yield mu, eta, chi, phi
+ return
+ for phi in phi_vals:
+ a = N_phi[0, 0] * cos(phi) + N_phi[1, 0] * sin(phi)
+ chi = atan2(N_phi[2, 0] * V[0, 0] - a * V[2, 0],
+ N_phi[2, 0] * V[2, 0] + a * V[0, 0]) # (60)
+ yield mu, eta, chi, phi
+
+ elif 'chi' in samp_constraints and 'phi' in samp_constraints:
+
+ chi = samp_constraints['chi']
+ phi = samp_constraints['phi']
+
+ CHI = calcCHI(chi)
+ PHI = calcPHI(phi)
+ V = CHI * PHI * N_phi # (62)
+
+ try:
+ bot = bound(V[2, 0] / sqrt(cos(qaz) ** 2 * cos(theta) ** 2 + sin(theta) ** 2))
+ except AssertionError:
+ return
+ eps = atan2(-cos(qaz) * cos(theta), sin(theta))
+ for mu in [asin(bot) + eps, pi - asin(bot) + eps]:
+ a = cos(theta) * sin(qaz)
+ b = -cos(theta) * sin(mu) * cos(qaz) + cos(mu) * sin(theta)
+ eta = atan2(V[1, 0] * a + V[0, 0] * b, V[0, 0] * a - V[1, 0]* b)
+
+ #a = -cos(mu) * cos(qaz) * sin(theta) + sin(mu) * cos(theta)
+ #b = cos(mu) * sin(qaz)
+ #psi = atan2(-V[2, 2] * a - V[2, 1] * b, V[2, 1] * a - V[2, 2] * b)
+ yield mu, eta, chi, phi
+
+ elif 'mu' in samp_constraints and 'phi' in samp_constraints:
+
+ mu = samp_constraints['mu']
+ phi = samp_constraints['phi']
+
+ F = y_rotation(qaz - pi/2.)
+ THETA = z_rotation(-theta)
+ V = calcMU(mu).T * F * THETA
+ E = calcPHI(phi) * N_phi
+
+ try:
+ bot = bound(-V[2, 0] / sqrt(E[0, 0]**2 + E[2, 0]**2))
+ except AssertionError:
+ return
+ eps = atan2(E[2, 0], E[0, 0])
+ for chi in [asin(bot) + eps, pi - asin(bot) + eps]:
+ a = E[0, 0] * cos(chi) + E[2, 0] * sin(chi)
+ eta = atan2(V[0, 0] * E[1, 0] - V[1, 0] * a, V[0, 0] * a + V[1, 0] * E[1, 0])
+ yield mu, eta, chi, phi
+ else:
+ raise DiffcalcException(
+ 'No code yet to handle this combination of 2 sample '
+ 'constraints and one detector!:' + str(samp_constraints))
+
+ def _filter_angle_limits(self, possible_solutions, filter_out_of_limits=True):
+ res = []
+ angle_names = self._hardware.get_axes_names()
+ for possible_solution in possible_solutions:
+ hw_sol = []
+ hw_possible_solution = self._geometry.internal_position_to_physical_angles(YouPosition(*possible_solution, unit='RAD'))
+ for name, value in zip(angle_names, hw_possible_solution):
+ hw_sol.append(self._hardware.cut_angle(name, value))
+ if filter_out_of_limits:
+ is_in_limits = all([self._hardware.is_axis_value_within_limits(name, value) for name, value in zip(angle_names, hw_sol)])
+ else:
+ is_in_limits = True
+ if is_in_limits:
+ sol = self._geometry.physical_angles_to_internal_position(tuple(hw_sol))
+ sol.changeToRadians()
+ res.append(sol.totuple())
+ return res
+
+def _mu_and_qaz_from_eta_chi_phi(eta, chi, phi, theta, h_phi):
+
+ h_phi_norm = normalised(h_phi) # (68,69)
+ h1, h2, h3 = h_phi_norm[0, 0], h_phi_norm[1, 0], h_phi_norm[2, 0]
+ a = sin(chi) * h2 * sin(phi) + sin(chi) * h1 * cos(phi) - cos(chi) * h3
+ b = (- cos(chi) * sin(eta) * h2 * sin(phi)
+ - cos(eta) * h1 * sin(phi) + cos(eta) * h2 * cos(phi)
+ - cos(chi) * sin(eta) * h1 * cos(phi)
+ - sin(chi) * sin(eta) * h3)
+ c = -sin(theta)
+ sin_bit = bound(c / sqrt(a * a + b * b))
+ mu1 = asin(sin_bit) - atan2(b, a)
+ mu2 = pi - asin(sin_bit) - atan2(b, a)
+
+ mu1 = cut_at_minus_pi(mu1)
+ mu2 = cut_at_minus_pi(mu2)
+
+ # TODO: This special case should be *removed* when the general case has shown
+ # to encompass it. It exists as fallback for a particular i16 experiment in
+ # May 2013 --RobW.
+# if eta == chi == 0:
+# logger.debug("Testing against simplified equations for eta == chi == 0")
+# a = - h3
+# b = - h1 * sin(phi) + h2 * cos(phi)
+# sin_bit = bound(c / sqrt(a * a + b * b))
+# mu_simplified = pi - asin(sin_bit) - atan2(b, a)
+# mu_simplified = cut_at_minus_pi(mu_simplified)
+# if not ne(mu_simplified, mu):
+# raise AssertionError("mu_simplified != mu , %f!=%f" % (mu_simplified, mu))
+
+
+ [MU, _, _, ETA, CHI, PHI] = create_you_matrices(mu1, None, None, eta, chi, phi)
+ h_lab = MU * ETA * CHI * PHI * h_phi # (11)
+ qaz1 = atan2(h_lab[0, 0] , h_lab[2, 0])
+
+ [MU, _, _, ETA, CHI, PHI] = create_you_matrices(mu2, None, None, eta, chi, phi)
+ h_lab = MU * ETA * CHI * PHI * h_phi # (11)
+ qaz2 = atan2(h_lab[0, 0] , h_lab[2, 0])
+
+ return (mu1, qaz1) , (mu2, qaz2)
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/you/constraints.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/you/constraints.py
new file mode 100755
index 0000000..1fe08d7
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/hkl/you/constraints.py
@@ -0,0 +1,377 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from math import pi
+
+try:
+ from numpy import matrix
+except ImportError:
+ from numjy import matrix
+
+from diffcalc.util import DiffcalcException, bold
+
+TODEG = 180 / pi
+TORAD = pi / 180
+
+NUNAME = 'gam'
+
+def filter_dict(d, keys):
+ """Return a copy of d containing only keys that are in keys"""
+ ##return {k: d[k] for k in keys} # requires Python 2.6
+ return dict((k, d[k]) for k in keys if k in d.keys())
+
+
+det_constraints = ('delta', NUNAME, 'qaz', 'naz')
+ref_constraints = ('a_eq_b', 'alpha', 'beta', 'psi')
+samp_constraints = ('mu', 'eta', 'chi', 'phi', 'mu_is_' + NUNAME)
+
+valueless_constraints = ('a_eq_b', 'mu_is_' + NUNAME)
+all_constraints = det_constraints + ref_constraints + samp_constraints
+
+
+number_single_sample = (len(det_constraints) * len(ref_constraints) *
+ len(samp_constraints))
+
+
+class YouConstraintManager(object):
+
+ def __init__(self, hardware, fixed_constraints = {}):
+ self._hardware = hardware
+ self._constrained = {}
+# self._tracking = []
+ self.n_phi = matrix([[0], [0], [1]])
+ self._hide_detector_constraint = False # default
+ self._fixed_samp_constraints = ()
+ self._fix_constraints(fixed_constraints)
+
+ def __str__(self):
+ lines = []
+# TODO: Put somewhere with access to UB matrix!
+# WIDTH = 13
+# n_phi = self.n_phi
+# fmt = "% 9.5f % 9.5f % 9.5f"
+# lines.append(" n_phi:".ljust(WIDTH) +
+# fmt % (n_phi[0, 0], n_phi[1, 0], n_phi[2, 0]))
+# if self._getUBMatrix():
+# n_cryst = self._getUMatrix().I * self.n_phi
+# lines.append(" n_cryst:".ljust(WIDTH) +
+# fmt % (n_cryst[0, 0], n_cryst[1, 0], n_cryst[2, 0]))
+# n_recip = self._getUBMatrix().I * self.n_phi
+# lines.append(" n_recip:".ljust(WIDTH) +
+# fmt % (n_recip[0, 0], n_recip[1, 0], n_recip[2, 0]))
+# else:
+# lines.append(
+# " n_cryst:".ljust(WIDTH) + ' "<<< No U matrix >>>"')
+# lines.append(
+# " n_recip:".ljust(WIDTH) + ' "<<< No UB matrix >>>"')
+
+ lines.extend(self.build_display_table_lines())
+ lines.append("")
+ lines.extend(self.report_constraints_lines())
+ lines.append("")
+ if (self.is_fully_constrained() and
+ not self.is_current_mode_implemented()):
+ lines.append(
+ " Sorry, this constraint combination is not implemented")
+ lines.append(" Type 'help con' for available combinations")
+ else:
+ lines.append(" Type 'help con' for instructions") # okay
+ return '\n'.join(lines)
+
+ @property
+ def available_constraint_names(self):
+ """list of all available constraints"""
+ return all_constraints
+
+ @property
+ def settable_constraint_names(self):
+ """list of all available constraints that have settable values"""
+ all_copy = list(all_constraints)
+ for valueless in valueless_constraints:
+ all_copy.remove(valueless)
+ return all_copy
+
+ @property
+ def all(self): # @ReservedAssignment
+ """dictionary of all constrained values"""
+ return self._constrained.copy()
+
+ @property
+ def detector(self):
+ """dictionary of constrained detector circles"""
+ return filter_dict(self.all, det_constraints[:-1])
+
+ @property
+ def reference(self):
+ """dictionary of constrained reference circles"""
+ return filter_dict(self.all, ref_constraints)
+
+ @property
+ def sample(self):
+ """dictionary of constrained sample circles"""
+ return filter_dict(self.all, samp_constraints)
+
+ @property
+ def naz(self):
+ """dictionary with naz and value if constrained"""
+ return filter_dict(self.all, ('naz',))
+
+ @property
+ def constrained_names(self):
+ """ordered tuple of constained circles"""
+ names = self.all.keys()
+ names.sort(key=lambda name: list(all_constraints).index(name))
+ return tuple(names)
+
+ def _fix_constraints(self, fixed_constraints):
+ for name in fixed_constraints:
+ self.constrain(name)
+ self.set_constraint(name, fixed_constraints[name])
+
+ if self.detector or self.naz:
+ self._hide_detector_constraint = True
+
+ fixed_samp_constraints = list(self.sample.keys())
+ if 'mu' in self.sample or NUNAME in self.detector:
+ fixed_samp_constraints.append('mu_is_' + NUNAME)
+ self._fixed_samp_constraints = tuple(fixed_samp_constraints)
+
+
+ def is_constrained(self, name):
+ return name in self._constrained
+
+ def get_value(self, name):
+ return self._constrained[name]
+
+ def build_display_table_lines(self):
+ unfixed_samp_constraints = list(samp_constraints)
+ for name in self._fixed_samp_constraints:
+ unfixed_samp_constraints.remove(name)
+ if self._hide_detector_constraint:
+ constraint_types = (ref_constraints, unfixed_samp_constraints)
+ else:
+ constraint_types = (det_constraints, ref_constraints,
+ unfixed_samp_constraints)
+ num_rows = max([len(col) for col in constraint_types])
+ max_name_width = max(
+ [len(name) for name in sum(constraint_types[:-1], ())])
+
+ cells = []
+
+ header_cells = []
+ if not self._hide_detector_constraint:
+ header_cells.append(bold(' ' + 'DET'.ljust(max_name_width)))
+ header_cells.append(bold(' ' + 'REF'.ljust(max_name_width)))
+ header_cells.append(bold(' ' + 'SAMP'))
+ cells.append(header_cells)
+
+ underline_cells = [' ' + '-' * max_name_width] * len(constraint_types)
+ cells.append(underline_cells)
+
+ for n_row in range(num_rows):
+ row_cells = []
+ for col in constraint_types:
+ name = col[n_row] if n_row < len(col) else ''
+ row_cells.append(self._label_constraint(name))
+ row_cells.append(('%-' + str(max_name_width) + 's') % name)
+ cells.append(row_cells)
+
+ lines = [' '.join(row_cells).rstrip() for row_cells in cells]
+ return lines
+
+ def _report_constraint(self, name):
+ val = self.get_constraint(name)
+ if name in valueless_constraints:
+ return " %s" % name
+ else:
+ if val is None:
+ return "! %-5s: ---" % name
+ else:
+ return " %-5s: %.4f" % (name, val)
+
+ def report_constraints_lines(self):
+ lines = []
+ required = 3 - len(self.all)
+ if required == 0:
+ pass
+ elif required == 1:
+ lines.append('! 1 more constraint required')
+ else:
+ lines.append('! %d more constraints required' % required)
+ constraints = []
+ constraints.extend(self.detector.keys())
+ constraints.extend(self.naz.keys())
+ constraints.extend(self.reference.keys())
+ constraints.extend(sorted(self.sample.keys()))
+ for name in constraints:
+ lines.append(self._report_constraint(name))
+ return lines
+
+ def is_fully_constrained(self):
+ return len(self.all) == 3
+
+ def is_current_mode_implemented(self):
+ if not self.is_fully_constrained():
+ raise ValueError("Three constraints required")
+
+ if len(self.sample) == 3:
+ if set(self.sample.keys()) == set(['chi', 'phi', 'eta']):
+ return True
+ return False
+
+ if len(self.sample) == 1:
+ return True
+
+ if len(self.reference) == 1:
+ return (set(self.sample.keys()) == set(['chi', 'phi']) or
+ set(self.sample.keys()) == set(['chi', 'eta']) or
+ set(self.sample.keys()) == set(['chi', 'mu']) or
+ set(self.sample.keys()) == set(['mu', 'eta']))
+
+ if len(self.detector) == 1:
+ return (set(self.sample.keys()) == set(['chi', 'phi']) or
+ set(self.sample.keys()) == set(['mu', 'eta']) or
+ set(self.sample.keys()) == set(['mu', 'phi'])
+ )
+
+ return False
+
+
+ def _label_constraint(self, name):
+ if name == '':
+ label = ' '
+# elif self.is_tracking(name): # implies constrained
+# label = '~~> '
+ elif (self.is_constrained(name) and (self.get_value(name) is None) and
+ name not in valueless_constraints):
+ label = 'o->'
+ elif self.is_constrained(name):
+ label = '-->'
+ else:
+ label = ' '
+ return label
+
+ def constrain(self, name):
+ if self.is_constraint_fixed(name):
+ raise DiffcalcException('%s is not a valid constraint name' % name)
+ if name in self.all:
+ return "%s is already constrained." % name.capitalize()
+ elif name in det_constraints:
+ return self._constrain_detector(name)
+ elif name in ref_constraints:
+ return self._constrain_reference(name)
+ elif name in samp_constraints:
+ return self._constrain_sample(name)
+ else:
+ raise DiffcalcException("%s is not a valid constraint name. Type 'con' for a table of constraint name" % name)
+
+ def is_constraint_fixed(self, name):
+ return ((name in det_constraints and self._hide_detector_constraint) or
+ (name in samp_constraints and name in self._fixed_samp_constraints))
+
+ def _constrain_detector(self, name):
+ if self.naz:
+ del self._constrained['naz']
+ self._constrained[name] = None
+ return 'Naz constraint replaced.'
+ elif self.detector:
+ constrained_name = self.detector.keys()[0]
+ del self._constrained[constrained_name]
+ self._constrained[name] = None
+ return'%s constraint replaced.' % constrained_name.capitalize()
+ elif len(self.all) == 3: # and no detector
+ raise self._could_not_constrain_exception(name)
+ else:
+ self._constrained[name] = None
+
+ def _could_not_constrain_exception(self, name):
+ return DiffcalcException(
+ "%s could not be constrained. First un-constrain one of the\n"
+ "angles %s, %s or %s (with 'uncon')" %
+ ((name.capitalize(),) + self.constrained_names))
+
+ def _constrain_reference(self, name):
+ if self.reference:
+ constrained_name = self.reference.keys()[0]
+ del self._constrained[constrained_name]
+ self._constrained[name] = None
+ return '%s constraint replaced.' % constrained_name.capitalize()
+ elif len(self.all) == 3: # and no reference
+ raise self._could_not_constrain_exception(name)
+ else:
+ self._constrained[name] = None
+
+ def _constrain_sample(self, name):
+ if len(self._constrained) < 3:
+ # okay, more to add
+ self._constrained[name] = None
+ # else: three constraints are set
+ elif len(self.sample) == 1:
+ # (detector and reference constraints set)
+ # it is clear which sample constraint to remove
+ constrained_name = self.sample.keys()[0]
+ del self._constrained[constrained_name]
+ self._constrained[name] = None
+ return '%s constraint replaced.' % constrained_name.capitalize()
+ else:
+ raise self._could_not_constrain_exception(name)
+
+ def unconstrain(self, name):
+ if self.is_constraint_fixed(name):
+ raise DiffcalcException('%s is not a valid constraint name')
+ if name in self._constrained:
+ del self._constrained[name]
+ else:
+ return "%s was not already constrained." % name.capitalize()
+
+ def _check_constraint_settable(self, name):
+ if name not in all_constraints:
+ raise DiffcalcException(
+ 'Could not set %(name)s. This is not an available '
+ 'constraint.' % locals())
+ elif name not in self.all.keys():
+ raise DiffcalcException(
+ 'Could not set %(name)s. This is not currently '
+ 'constrained.' % locals())
+ elif name in valueless_constraints:
+ raise DiffcalcException(
+ 'Could not set %(name)s. This constraint takes no '
+ 'value.' % locals())
+
+ def clear_constraints(self):
+ self._constrained = {}
+
+ def set_constraint(self, name, value): # @ReservedAssignment
+ if self.is_constraint_fixed(name):
+ raise DiffcalcException('%s is not a valid constraint name')
+ self._check_constraint_settable(name)
+# if name in self._tracking:
+# raise DiffcalcException(
+# "Could not set %s as this constraint is configured to track "
+# "its associated\nphysical angle. First remove this tracking "
+# "(use 'untrack %s').""" % (name, name))
+ old_value = self.get_constraint(name)
+ old = str(old_value) if old_value is not None else '---'
+ self._constrained[name] = float(value) * TORAD
+ new = str(value)
+ return "%(name)s : %(old)s --> %(new)s" % locals()
+
+ def get_constraint(self, name):
+ value = self.all[name]
+ return None if value is None else value * TODEG
+
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/you/geometry.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/you/geometry.py
new file mode 100755
index 0000000..d8d034a
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/hkl/you/geometry.py
@@ -0,0 +1,219 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from math import pi
+
+from diffcalc.util import AbstractPosition, DiffcalcException
+
+TORAD = pi / 180
+TODEG = 180 / pi
+from diffcalc.util import x_rotation, z_rotation, y_rotation
+
+from diffcalc.hkl.you.constraints import NUNAME
+
+class YouGeometry(object):
+
+ def __init__(self, name, fixed_constraints, beamline_axes_transform=None):
+ self.name = name
+ self.fixed_constraints = fixed_constraints
+ # beamline_axes_transform matrix is composed of the diffcalc basis vector coordinates
+ # in the beamline coordinate system, i.e. it transforms the beamline coordinate system
+ # into the reference diffcalc one.
+ self.beamline_axes_transform = beamline_axes_transform
+
+ def physical_angles_to_internal_position(self, physical_angle_tuple):
+ raise NotImplementedError()
+
+ def internal_position_to_physical_angles(self, internal_position):
+ raise NotImplementedError()
+
+ def create_position(self, *args):
+ return YouPosition(*args, unit='DEG')
+
+
+#==============================================================================
+#==============================================================================
+# Geometry plugins for use with 'You' hkl calculation engine
+#==============================================================================
+#==============================================================================
+
+
+class SixCircle(YouGeometry):
+ def __init__(self, beamline_axes_transform=None):
+ YouGeometry.__init__(self, 'sixc', {}, beamline_axes_transform)
+
+ def physical_angles_to_internal_position(self, physical_angle_tuple):
+ # mu, delta, nu, eta, chi, phi
+ return YouPosition(*physical_angle_tuple, unit='DEG')
+
+ def internal_position_to_physical_angles(self, internal_position):
+ clone_position = internal_position.clone()
+ clone_position.changeToDegrees()
+ return clone_position.totuple()
+
+
+class FourCircle(YouGeometry):
+ """For a diffractometer with angles:
+ delta, eta, chi, phi
+ """
+ def __init__(self, beamline_axes_transform=None):
+ YouGeometry.__init__(self, 'fourc', {'mu': 0, NUNAME: 0}, beamline_axes_transform)
+
+ def physical_angles_to_internal_position(self, physical_angle_tuple):
+ # mu, delta, nu, eta, chi, phi
+ delta, eta, chi, phi = physical_angle_tuple
+ return YouPosition(0, delta, 0, eta, chi, phi, 'DEG')
+
+ def internal_position_to_physical_angles(self, internal_position):
+ clone_position = internal_position.clone()
+ clone_position.changeToDegrees()
+ _, delta, _, eta, chi, phi = clone_position.totuple()
+ return delta, eta, chi, phi
+
+
+class FiveCircle(YouGeometry):
+ """For a diffractometer with angles:
+ delta, nu, eta, chi, phi
+ """
+ def __init__(self, beamline_axes_transform=None):
+ YouGeometry.__init__(self, 'fivec', {'mu': 0}, beamline_axes_transform)
+
+ def physical_angles_to_internal_position(self, physical_angle_tuple):
+ # mu, delta, nu, eta, chi, phi
+ delta, nu, eta, chi, phi = physical_angle_tuple
+ return YouPosition(0, delta, nu, eta, chi, phi, 'DEG')
+
+ def internal_position_to_physical_angles(self, internal_position):
+ clone_position = internal_position.clone()
+ clone_position.changeToDegrees()
+ _, delta, nu, eta, chi, phi = clone_position.totuple()
+ return delta, nu, eta, chi, phi
+
+
+#==============================================================================
+
+
+def create_you_matrices(mu=None, delta=None, nu=None, eta=None, chi=None,
+ phi=None):
+ """
+ Create transformation matrices from H. You's paper.
+ """
+ MU = None if mu is None else calcMU(mu)
+ DELTA = None if delta is None else calcDELTA(delta)
+ NU = None if nu is None else calcNU(nu)
+ ETA = None if eta is None else calcETA(eta)
+ CHI = None if chi is None else calcCHI(chi)
+ PHI = None if phi is None else calcPHI(phi)
+ return MU, DELTA, NU, ETA, CHI, PHI
+
+
+def calcNU(nu):
+ return x_rotation(nu)
+
+
+def calcDELTA(delta):
+ return z_rotation(-delta)
+
+
+def calcMU(mu_or_alpha):
+ return x_rotation(mu_or_alpha)
+
+
+def calcETA(eta):
+ return z_rotation(-eta)
+
+
+def calcCHI(chi):
+ return y_rotation(chi)
+
+
+def calcPHI(phi):
+ return z_rotation(-phi)
+
+
+def you_position_names():
+ return ('mu', 'delta', NUNAME, 'eta', 'chi', 'phi')
+
+class YouPosition(AbstractPosition):
+
+ def __init__(self, mu, delta, nu, eta, chi, phi, unit):
+ self.mu = mu
+ self.delta = delta
+ self.nu = nu
+ self.eta = eta
+ self.chi = chi
+ self.phi = phi
+ if unit not in ['DEG', 'RAD']:
+ raise DiffcalcException("Invalid angle unit value %s." % str(unit))
+ else:
+ self.unit = unit
+
+ def clone(self):
+ return YouPosition(self.mu, self.delta, self.nu, self.eta, self.chi,
+ self.phi, self.unit)
+
+ def changeToRadians(self):
+ if self.unit == 'DEG':
+ self.mu *= TORAD
+ self.delta *= TORAD
+ self.nu *= TORAD
+ self.eta *= TORAD
+ self.chi *= TORAD
+ self.phi *= TORAD
+ self.unit = 'RAD'
+ elif self.unit == 'RAD':
+ return
+ else:
+ raise DiffcalcException("Invalid angle unit value %s." % str(self.unit))
+
+ def changeToDegrees(self):
+ if self.unit == 'RAD':
+ self.mu *= TODEG
+ self.delta *= TODEG
+ self.nu *= TODEG
+ self.eta *= TODEG
+ self.chi *= TODEG
+ self.phi *= TODEG
+ self.unit = 'DEG'
+ elif self.unit == 'DEG':
+ return
+ else:
+ raise DiffcalcException("Invalid angle unit value %s." % str(self.unit))
+
+ def totuple(self):
+ return (self.mu, self.delta, self.nu, self.eta, self.chi, self.phi)
+
+ def __str__(self):
+ mu, delta, nu, eta, chi, phi = self.totuple()
+ return ("YouPosition(mu %r delta: %r nu: %r eta: %r chi: %r phi: %r) in %s"
+ % (mu, delta, nu, eta, chi, phi, self.unit))
+
+ def __eq__(self, other):
+ return self.totuple() == other.totuple()
+
+
+class WillmottHorizontalPosition(YouPosition):
+
+ def __init__(self, delta=None, gamma=None, omegah=None, phi=None):
+ self.mu = 0
+ self.delta = delta
+ self.nu = gamma
+ self.eta = omegah
+ self.chi = -90
+ self.phi = phi
+ self.unit= 'DEG'
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/hkl/you/hkl.py b/script/__Lib/diffcalc-2.1/diffcalc/hkl/you/hkl.py
new file mode 100755
index 0000000..fe3e806
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/hkl/you/hkl.py
@@ -0,0 +1,187 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from __future__ import absolute_import
+
+from diffcalc.hkl.common import getNameFromScannableOrString
+from diffcalc.util import command
+from diffcalc.hkl.you.calc import YouHklCalculator
+from diffcalc import settings
+
+
+
+import diffcalc.ub.ub
+from diffcalc.hkl.you.constraints import YouConstraintManager
+
+__all__ = ['allhkl', 'con', 'uncon', 'hklcalc', 'constraint_manager']
+
+
+_fixed_constraints = settings.geometry.fixed_constraints # @UndefinedVariable
+
+constraint_manager = YouConstraintManager(settings.hardware, _fixed_constraints)
+
+hklcalc = YouHklCalculator(
+ diffcalc.ub.ub.ubcalc, settings.geometry, settings.hardware, constraint_manager)
+
+
+def __str__(self):
+ return hklcalc.__str__()
+
+@command
+def con(*args):
+ """
+ con -- list available constraints and values
+ con {val} -- constrains and optionally sets one constraint
+ con {val} {val} {val} -- clears and then fully constrains
+
+ Select three constraints using 'con' and 'uncon'. Choose up to one
+ from each of the sample and detector columns and up to three from
+ the sample column.
+
+ Not all constraint combinations are currently available:
+
+ 1 x samp: all 80 of 80
+
+ 2 x samp and 1 x ref: chi & phi
+ chi & eta
+ chi & mu
+ mu & eta (4 of 6)
+
+ 2 x samp and 1 x det: chi & phi
+ mu & eta
+ mu & phi (3 of 6)
+
+ 3 x samp: eta, chi & phi (1 of 4)
+
+ See also 'uncon'
+ """
+ args = list(args)
+ msg = _handle_con(args)
+ if (hklcalc.constraints.is_fully_constrained() and
+ not hklcalc.constraints.is_current_mode_implemented()):
+ msg += ("\n\nWARNING:. The selected constraint combination is valid but "
+ "is not implemented.\n\nType 'help con' to see implemented combinations")
+
+ if msg:
+ print msg
+
+def _handle_con(args):
+ if not args:
+ return hklcalc.constraints.__str__()
+
+ if len(args) > 6:
+ raise TypeError("Unexpected args: " + str(args))
+
+ cons_value_pairs = []
+ while args:
+ scn_or_str = args.pop(0)
+ name = getNameFromScannableOrString(scn_or_str)
+ if args and isinstance(args[0], (int, long, float)):
+ value = args.pop(0)
+ else:
+ try:
+ value = settings.hardware.get_position_by_name(name)
+ except ValueError:
+ value = None
+ cons_value_pairs.append((name, value))
+
+ if len(cons_value_pairs) == 1:
+ pass
+ elif len(cons_value_pairs) == 3:
+ hklcalc.constraints.clear_constraints()
+ else:
+ raise TypeError("Either one or three constraints must be specified")
+ for name, value in cons_value_pairs:
+ hklcalc.constraints.constrain(name)
+ if value is not None:
+ hklcalc.constraints.set_constraint(name, value)
+ return '\n'.join(hklcalc.constraints.report_constraints_lines())
+
+
+@command
+def uncon(scn_or_string):
+ """uncon -- remove constraint
+
+ See also 'con'
+ """
+ name = getNameFromScannableOrString(scn_or_string)
+ hklcalc.constraints.unconstrain(name)
+ print '\n'.join(hklcalc.constraints.report_constraints_lines())
+
+@command
+def allhkl(hkl, wavelength=None):
+ """allhkl [h k l] -- print all hkl solutions ignoring limits
+
+ """
+ hardware = hklcalc._hardware
+ geometry = hklcalc._geometry
+ if wavelength is None:
+ wavelength = hardware.get_wavelength()
+ h, k, l = hkl
+ pos_virtual_angles_pairs = hklcalc.hkl_to_all_angles(
+ h, k, l, wavelength)
+ cells = []
+ # virtual_angle_names = list(pos_virtual_angles_pairs[0][1].keys())
+ # virtual_angle_names.sort()
+ virtual_angle_names = ['qaz', 'psi', 'naz', 'tau', 'theta', 'alpha', 'beta']
+ header_cells = list(hardware.get_axes_names()) + [' '] + virtual_angle_names
+ cells.append(['%9s' % s for s in header_cells])
+ cells.append([''] * len(header_cells))
+
+
+ for pos, virtual_angles in pos_virtual_angles_pairs:
+ row_cells = []
+
+
+ angle_tuple = geometry.internal_position_to_physical_angles(pos)
+ angle_tuple = hardware.cut_angles(angle_tuple)
+ for val in angle_tuple:
+ row_cells.append('%9.4f' % val)
+
+ row_cells.append('|')
+
+ for name in virtual_angle_names:
+ row_cells.append('%9.4f' % virtual_angles[name])
+ cells.append(row_cells)
+
+
+ column_widths = []
+ for col in range(len(cells[0])):
+ widths = []
+ for row in range(len(cells)):
+ cell = cells[row][col]
+ width = len(cell.strip())
+ widths.append(width)
+ column_widths.append(max(widths))
+
+ lines = []
+ for row_cells in cells:
+ trimmed_row_cells = []
+ for cell, width in zip(row_cells, column_widths):
+ trimmed_cell = cell.strip().rjust(width)
+ trimmed_row_cells.append(trimmed_cell)
+ lines.append(' '.join(trimmed_row_cells))
+ print '\n'.join(lines)
+
+
+commands_for_help = ['Constraints',
+ con,
+ uncon,
+ 'Hkl',
+ allhkl
+ ]
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/log.py b/script/__Lib/diffcalc-2.1/diffcalc/log.py
new file mode 100755
index 0000000..ac4f1af
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/log.py
@@ -0,0 +1,27 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from __future__ import absolute_import
+
+import logging
+import getpass
+
+logging.basicConfig(format="%(asctime)s %(levelname)s:%(name)s:%(message)s",
+ datefmt='%m/%d/%Y %I:%M:%S',
+ filename='/tmp/diffcalc_%s.log' % getpass.getuser(),
+ level=logging.DEBUG)
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/settings.py b/script/__Lib/diffcalc-2.1/diffcalc/settings.py
new file mode 100755
index 0000000..5221212
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/settings.py
@@ -0,0 +1,24 @@
+'''
+Created on Aug 5, 2013
+
+@author: walton
+'''
+import os
+
+from diffcalc.ub.persistence import UbCalculationNonPersister
+
+# These should be by the user *before* importing other modules
+geometry = None
+hardware = None
+ubcalc_persister = UbCalculationNonPersister()
+
+axes_scannable_group = None
+energy_scannable = None
+energy_scannable_multiplier_to_get_KeV=1
+
+
+# These will be set by dcyou, dcvlieg or dcwillmot
+ubcalc_strategy = None
+angles_to_hkl_function = None # Used by checkub to avoid coupling it to an hkl module
+include_sigtau=False
+include_reference=False
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/ub/__init__.py b/script/__Lib/diffcalc-2.1/diffcalc/ub/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/ub/calc.py b/script/__Lib/diffcalc-2.1/diffcalc/ub/calc.py
new file mode 100755
index 0000000..d71bc5e
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/ub/calc.py
@@ -0,0 +1,854 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from diffcalc.ub.calcstate import decode_ubcalcstate
+from diffcalc.ub.calcstate import UBCalcState
+from diffcalc.ub.crystal import CrystalUnderTest
+from diffcalc.ub.reflections import ReflectionList
+from diffcalc.ub.persistence import UBCalculationJSONPersister, UBCalculationPersister
+from diffcalc.util import DiffcalcException, cross3, dot3, bold, xyz_rotation,\
+ bound
+from math import acos, cos, sin, pi
+from diffcalc.ub.reference import YouReference
+from diffcalc.ub.orientations import OrientationList
+
+try:
+ from numpy import matrix, hstack
+ from numpy.linalg import norm
+except ImportError:
+ from numjy import matrix, hstack
+ from numjy.linalg import norm
+
+SMALL = 1e-7
+TODEG = 180 / pi
+
+WIDTH = 13
+
+def z(num):
+ """Round to zero if small. This is useful to get rid of erroneous
+ minus signs resulting from float representation close to zero.
+ """
+ if abs(num) < SMALL:
+ num = 0
+ return num
+
+#The UB matrix is used to find or set the orientation of a set of
+#planes described by an hkl vector. The U matrix can be used to find
+#or set the orientation of the crystal lattices' y axis. If there is
+#crystal miscut the crystal lattices y axis is not parallel to the
+#crystals optical surface normal. For surface diffraction experiments,
+#where not only the crystal lattice must be oriented appropriately but
+#so must the crystal's optical surface, two angles tau and sigma are
+#used to describe the difference between the two. Sigma is (minus) the
+#ammount of chi axis rotation and tau (minus) the ammount of phi axis
+#rotation needed to move the surface normal into the direction of the
+
+
+class PaperSpecificUbCalcStrategy(object):
+
+ def calculate_q_phi(self, pos):
+ """Calculate hkl in the phi frame in units of 2 * pi / lambda from
+ pos object in radians"""
+ raise NotImplementedError()
+
+
+class UBCalculation:
+ """A UB matrix calculation for an experiment.
+
+ Contains the parameters for the _crystal under test, a list of measured
+ reflections and, if its been calculated, a UB matrix to be used by the rest
+ of the code.
+ """
+
+ def __init__(self, hardware, diffractometerPluginObject,
+ persister, strategy, include_sigtau=True, include_reference=True):
+
+ # The diffractometer geometry is required to map the internal angles
+ # into those used by this diffractometer (for display only)
+
+ self._hardware = hardware
+ self._geometry = diffractometerPluginObject
+ self._persister = persister
+ self._strategy = strategy
+ self.include_sigtau = include_sigtau
+ self.include_reference = include_reference
+ try:
+ self._ROT = diffractometerPluginObject.beamline_axes_transform
+ except AttributeError:
+ self._ROT = None
+ self._clear()
+
+ def _get_diffractometer_axes_names(self):
+ return self._hardware.get_axes_names()
+
+ def _clear(self, name=None):
+ # NOTE the Diffraction calculator is expecting this object to exist in
+ # the long run. We can't remove this entire object, and recreate it.
+ # It also contains a required link to the angle calculator.
+ reflist = ReflectionList(self._geometry, self._get_diffractometer_axes_names())
+ orientlist = OrientationList()
+ reference = YouReference(self._get_UB)
+ self._state = UBCalcState(name=name, reflist=reflist, orientlist=orientlist, reference=reference)
+ self._U = None
+ self._UB = None
+ self._state.configure_calc_type()
+
+### State ###
+ def start_new(self, name):
+ """start_new(name) --- creates a new blank ub calculation"""
+ # Create storage object if name does not exist (TODO)
+ if name in self._persister.list():
+ print ("No UBCalculation started: There is already a calculation "
+ "called: " + name)
+ print "Saved calculations: " + repr(self._persister.list())
+ return
+ self._clear(name)
+ self.save()
+
+ def load(self, name):
+ state = self._persister.load(name)
+ if isinstance(self._persister, UBCalculationJSONPersister):
+ self._state = decode_ubcalcstate(state, self._geometry, self._get_diffractometer_axes_names())
+ self._state.reference.get_UB = self._get_UB
+ elif isinstance(self._persister, UBCalculationPersister):
+ self._state = state
+ else:
+ raise Exception('Unexpected persister type: ' + str(self._persister))
+ if self._state.manual_U is not None:
+ self._U = self._state.manual_U
+ self._UB = self._U * self._state.crystal.B
+ self.save()
+ elif self._state.manual_UB is not None:
+ self._UB = self._state.manual_UB
+ self.save()
+ elif self._state.or0 is not None:
+ if self._state.or1 is None:
+ self.calculate_UB_from_primary_only()
+ else:
+ if self._state.reflist:
+ self.calculate_UB()
+ elif self._state.orientlist:
+ self.calculate_UB_from_orientation()
+ else:
+ pass
+ else:
+ pass
+
+ def save(self):
+ self.saveas(self._state.name)
+
+ def saveas(self, name):
+ self._state.name = name
+ self._persister.save(self._state, name)
+
+ def listub(self):
+ return self._persister.list()
+
+ def listub_metadata(self):
+ return self._persister.list_metadata()
+
+ def remove(self, name):
+ self._persister.remove(name)
+ if self._state == name:
+ self._clear(name)
+
+ def getState(self):
+ return self._state.getState()
+
+ def __str__(self):
+
+ if self._state.name is None:
+ return "<<< No UB calculation started >>>"
+ lines = []
+ lines.append(bold("UBCALC"))
+ lines.append("")
+ lines.append(
+ " name:".ljust(WIDTH) + self._state.name.rjust(9))
+
+ if self.include_sigtau:
+ lines.append("")
+ lines.append(
+ " sigma:".ljust(WIDTH) + ("% 9.5f" % self._state.sigma).rjust(9))
+ lines.append(
+ " tau:".ljust(WIDTH) + ("% 9.5f" % self._state.tau).rjust(9))
+
+ if self.include_reference:
+ lines.append("")
+ ub_calculated = self._UB is not None
+ lines.extend(self._state.reference.repr_lines(ub_calculated, WIDTH, self._ROT))
+
+ lines.append("")
+ lines.append(bold("CRYSTAL"))
+ lines.append("")
+
+ if self._state.crystal is None:
+ lines.append(" <<< none specified >>>")
+ else:
+ lines.extend(self._state.crystal.str_lines())
+
+ lines.append("")
+ lines.append(bold("UB MATRIX"))
+ lines.append("")
+
+ if self._UB is None:
+ lines.append(" <<< none calculated >>>")
+ else:
+ lines.extend(self.str_lines_u())
+ lines.append("")
+ lines.extend(self.str_lines_u_angle_and_axis())
+ lines.append("")
+ lines.extend(self.str_lines_ub())
+
+ lines.append("")
+ lines.append(bold("REFLECTIONS"))
+ lines.append("")
+
+ lines.extend(self._state.reflist.str_lines())
+
+ lines.append("")
+ lines.append(bold("CRYSTAL ORIENTATIONS"))
+ lines.append("")
+
+ lines.extend(self._state.orientlist.str_lines(R=self._ROT))
+
+ return '\n'.join(lines)
+
+ def str_lines_u(self):
+ lines = []
+ fmt = "% 9.5f % 9.5f % 9.5f"
+ try:
+ U = self._ROT.I * self.U * self._ROT
+ except AttributeError:
+ U = self.U
+ lines.append(" U matrix:".ljust(WIDTH) +
+ fmt % (z(U[0, 0]), z(U[0, 1]), z(U[0, 2])))
+ lines.append(' ' * WIDTH + fmt % (z(U[1, 0]), z(U[1, 1]), z(U[1, 2])))
+ lines.append(' ' * WIDTH + fmt % (z(U[2, 0]), z(U[2, 1]), z(U[2, 2])))
+ return lines
+
+ def str_lines_u_angle_and_axis(self):
+ lines = []
+ fmt = "% 9.5f % 9.5f % 9.5f"
+ y = matrix('0; 0; 1')
+ try:
+ rotation_axis = cross3(self._ROT * y, self._ROT * self.U * y)
+ except TypeError:
+ rotation_axis = cross3(y, self.U * y)
+ if abs(norm(rotation_axis)) < SMALL:
+ lines.append(" miscut angle:".ljust(WIDTH) + " 0")
+ else:
+ rotation_axis = rotation_axis * (1 / norm(rotation_axis))
+ cos_rotation_angle = dot3(y, self.U * y)
+ rotation_angle = acos(cos_rotation_angle)
+
+ lines.append(" miscut:")
+ lines.append(" angle:".ljust(WIDTH) + "% 9.5f" % (rotation_angle * TODEG))
+ lines.append(" axis:".ljust(WIDTH) + fmt % tuple((rotation_axis.T).tolist()[0]))
+
+ return lines
+
+ def str_lines_ub(self):
+ lines = []
+ fmt = "% 9.5f % 9.5f % 9.5f"
+ try:
+ RI = self._ROT.I
+ B = self._state.crystal.B
+ UB = RI * self.UB * B.I * self._ROT * B
+ except AttributeError:
+ UB = self.UB
+ lines.append(" UB matrix:".ljust(WIDTH) +
+ fmt % (z(UB[0, 0]), z(UB[0, 1]), z(UB[0, 2])))
+ lines.append(' ' * WIDTH + fmt % (z(UB[1, 0]), z(UB[1, 1]), z(UB[1, 2])))
+ lines.append(' ' * WIDTH + fmt % (z(UB[2, 0]), z(UB[2, 1]), z(UB[2, 2])))
+ return lines
+
+ @property
+ def name(self):
+ return self._state.name
+### Lattice ###
+
+ def set_lattice(self, name, *shortform):
+ """
+ Converts a list shortform crystal parameter specification to a six-long
+ tuple returned as . Returns None if wrong number of input args. See
+ set_lattice() for a description of the shortforms supported.
+
+ shortformLattice -- a tuple as follows:
+ [a] - assumes cubic
+ [a,b]) - assumes tetragonal
+ [a,b,c]) - assumes ortho
+ [a,b,c,gam]) - assumes mon/hex gam different from 90.
+ [a,b,c,alp,bet,gam]) - for arbitrary
+ where all measurements in angstroms and angles in degrees
+ """
+ self._set_lattice_without_saving(name, *shortform)
+ self.save()
+
+ def _set_lattice_without_saving(self, name, *shortform):
+ sf = shortform
+ if len(sf) == 1:
+ fullform = (sf[0], sf[0], sf[0], 90., 90., 90.) # cubic
+ elif len(sf) == 2:
+ fullform = (sf[0], sf[0], sf[1], 90., 90., 90.) # tetragonal
+ elif len(sf) == 3:
+ fullform = (sf[0], sf[1], sf[2], 90., 90., 90.) # ortho
+ elif len(sf) == 4:
+ fullform = (sf[0], sf[1], sf[2], 90., 90., sf[3]) # mon/hex gam
+ # not 90
+ elif len(sf) == 5:
+ raise ValueError("wrong length input to set_lattice")
+ elif len(sf) == 6:
+ fullform = sf # triclinic/arbitrary
+ else:
+ raise ValueError("wrong length input to set_lattice")
+ self._set_lattice(name, *fullform)
+
+ def _set_lattice(self, name, a, b, c, alpha, beta, gamma):
+ """set lattice parameters in degrees"""
+ if self._state.name is None:
+ raise DiffcalcException(
+ "Cannot set lattice until a UBCalcaluation has been started "
+ "with newubcalc")
+ self._state.crystal = CrystalUnderTest(name, a, b, c, alpha, beta, gamma)
+ # Clear U and UB if these exist
+ if self._U is not None: # (UB will also exist)
+ print "Warning: the old UB calculation has been cleared."
+ print " Use 'calcub' to recalculate with old reflections or"
+ print " 'orientub' to recalculate with old orientations."
+
+### Surface normal stuff ###
+
+ def _gettau(self):
+ """
+ Returns tau (in degrees): the (minus) ammount of phi axis rotation ,
+ that together with some chi axis rotation (minus sigma) brings the
+ optical surface normal parallel to the omega axis.
+ """
+ return self._state.tau
+
+ def _settau(self, tau):
+ self._state.tau = tau
+ self.save()
+
+ tau = property(_gettau, _settau)
+
+ def _getsigma(self):
+ """
+ Returns sigma (in degrees): the (minus) ammount of phi axis rotation ,
+ that together with some phi axis rotation (minus tau) brings the
+ optical surface normal parallel to the omega axis.
+ """
+ return self._state.sigma
+
+ def _setsigma(self, sigma):
+ self.state._sigma = sigma
+ self.save()
+
+ sigma = property(_getsigma, _setsigma)
+
+
+### Reference vector ###
+
+ def _get_n_phi(self):
+ return self._state.reference.n_phi
+
+ n_phi = property(_get_n_phi)
+
+ def set_n_phi_configured(self, n_phi):
+ try:
+ self._state.reference.n_phi_configured = self._ROT.I * n_phi
+ except AttributeError:
+ self._state.reference.n_phi_configured = n_phi
+ self.save()
+
+ def set_n_hkl_configured(self, n_hkl):
+ self._state.reference.n_hkl_configured = n_hkl
+ self.save()
+
+ def print_reference(self):
+ print '\n'.join(self._state.reference.repr_lines(self.is_ub_calculated(), R=self._ROT))
+
+### Reflections ###
+
+ def add_reflection(self, h, k, l, position, energy, tag, time):
+ """add_reflection(h, k, l, position, tag=None) -- adds a reflection
+
+ position is in degrees and in the systems internal representation.
+ """
+ if self._state.reflist is None:
+ raise DiffcalcException("No UBCalculation loaded")
+ self._state.reflist.add_reflection(h, k, l, position, energy, tag, time)
+ self.save() # incase autocalculateUbAndReport fails
+
+ # If second reflection has just been added then calculateUB
+ if len(self._state.reflist) == 2:
+ self._autocalculateUbAndReport()
+ self.save()
+
+ def edit_reflection(self, num, h, k, l, position, energy, tag, time):
+ """
+ edit_reflection(num, h, k, l, position, tag=None) -- adds a reflection
+
+ position is in degrees and in the systems internal representation.
+ """
+ if self._state.reflist is None:
+ raise DiffcalcException("No UBCalculation loaded")
+ self._state.reflist.edit_reflection(num, h, k, l, position, energy, tag, time)
+
+ # If first or second reflection has been changed and there are at least
+ # two reflections then recalculate UB
+ if (num == 1 or num == 2) and len(self._state.reflist) >= 2:
+ self._autocalculateUbAndReport()
+ self.save()
+
+ def get_reflection(self, num):
+ """--> ( [h, k, l], position, energy, tag, time
+ num starts at 1, position in degrees"""
+ return self._state.reflist.getReflection(num)
+
+ def get_reflection_in_external_angles(self, num):
+ """--> ( [h, k, l], position, energy, tag, time
+ num starts at 1, position in degrees"""
+ return self._state.reflist.get_reflection_in_external_angles(num)
+
+ def get_number_reflections(self):
+ return 0 if self._state.reflist is None else len(self._state.reflist)
+
+ def del_reflection(self, reflectionNumber):
+ self._state.reflist.removeReflection(reflectionNumber)
+ if ((reflectionNumber == 1 or reflectionNumber == 2) and
+ (self._U is not None)):
+ self._autocalculateUbAndReport()
+ self.save()
+
+ def swap_reflections(self, num1, num2):
+ self._state.reflist.swap_reflections(num1, num2)
+ if ((num1 == 1 or num1 == 2 or num2 == 1 or num2 == 2) and
+ (self._U is not None)):
+ self._autocalculateUbAndReport()
+ self.save()
+
+ def _autocalculateUbAndReport(self):
+ if len(self._state.reflist) < 2:
+ pass
+ elif self._state.crystal is None:
+ print ("Not calculating UB matrix as no lattice parameters have "
+ "been specified.")
+ elif not self._state.is_okay_to_autocalculate_ub:
+ print ("Not calculating UB matrix as it has been manually set. "
+ "Use 'calcub' to explicitly recalculate it.")
+ else: # okay to autocalculate
+ if self._UB is None:
+ print "Calculating UB matrix."
+ else:
+ print "Recalculating UB matrix."
+ self.calculate_UB()
+
+### Orientations ###
+
+ def add_orientation(self, h, k, l, x, y, z, tag, time):
+ """add_reflection(h, k, l, x, y, z, tag=None) -- adds a crystal orientation
+ """
+ if self._state.orientlist is None:
+ raise DiffcalcException("No UBCalculation loaded")
+ try:
+ xyz_rot = self._ROT * matrix([[x],[y],[z]])
+ xr, yr, zr = xyz_rot.T.tolist()[0]
+ self._state.orientlist.add_orientation(h, k, l, xr, yr, zr, tag, time)
+ except TypeError:
+ self._state.orientlist.add_orientation(h, k, l, x, y, z, tag, time)
+ self.save() # incase autocalculateUbAndReport fails
+
+ # If second reflection has just been added then calculateUB
+ if len(self._state.orientlist) == 2:
+ self._autocalculateOrientationUbAndReport()
+ self.save()
+
+ def edit_orientation(self, num, h, k, l, x, y, z, tag, time):
+ """
+ edit_orientation(num, h, k, l, x, y, z, tag=None) -- edit a crystal reflection """
+ if self._state.orientlist is None:
+ raise DiffcalcException("No UBCalculation loaded")
+ try:
+ xyz_rot = self._ROT * matrix([[x],[y],[z]])
+ xr, yr, zr = xyz_rot.T.tolist()[0]
+ self._state.orientlist.edit_orientation(num, h, k, l, xr, yr, zr, tag, time)
+ except TypeError:
+ self._state.orientlist.edit_orientation(num, h, k, l, x, y, z, tag, time)
+
+ # If first or second orientation has been changed and there are
+ # two orientations then recalculate UB
+ if (num == 1 or num == 2) and len(self._state.orientlist) == 2:
+ self._autocalculateOrientationUbAndReport()
+ self.save()
+
+ def get_orientation(self, num):
+ """--> ( [h, k, l], [x, y, z], tag, time )
+ num starts at 1"""
+ try:
+ hkl, xyz, tg, tm = self._state.orientlist.getOrientation(num)
+ xyz_rot = self._ROT.I * matrix([[xyz[0]],[xyz[1]],[xyz[2]]])
+ xyz_lst = xyz_rot.T.tolist()[0]
+ return hkl, xyz_lst, tg, tm
+ except AttributeError:
+ return self._state.orientlist.getOrientation(num)
+
+
+ def get_number_orientations(self):
+ return 0 if self._state.orientlist is None else len(self._state.reflist)
+
+ def del_orientation(self, orientationNumber):
+ self._state.orientlist.removeOrientation(orientationNumber)
+ if ((orientationNumber == 2) and (self._U is not None)):
+ self._autocalculateOrientationUbAndReport()
+ self.save()
+
+ def swap_orientations(self, num1, num2):
+ self._state.orientlist.swap_orientations(num1, num2)
+ if ((num1 == 2 or num2 == 2) and
+ (self._U is not None)):
+ self._autocalculateOrientationUbAndReport()
+ self.save()
+
+ def _autocalculateOrientationUbAndReport(self):
+ if len(self._state.orientlist) < 2:
+ pass
+ elif self._state.crystal is None:
+ print ("Not calculating UB matrix as no lattice parameters have "
+ "been specified.")
+ elif not self._state.is_okay_to_autocalculate_ub:
+ print ("Not calculating UB matrix as it has been manually set. "
+ "Use 'orientub' to explicitly recalculate it.")
+ else: # okay to autocalculate
+ if self._UB is None:
+ print "Calculating UB matrix."
+ else:
+ print "Recalculating UB matrix."
+ self.calculate_UB_from_orientation()
+
+# @property
+# def reflist(self):
+# return self._state.reflist
+### Calculations ###
+
+ def set_U_manually(self, m):
+ """Manually sets U. matrix must be 3*3 Jama or python matrix.
+ Turns off aution UB calcualtion."""
+
+ # Check matrix is a 3*3 Jama matrix
+ if m.__class__ != matrix:
+ m = matrix(m) # assume its a python matrix
+ if m.shape[0] != 3 or m.shape[1] != 3:
+ raise ValueError("Expects 3*3 matrix")
+
+ if self._UB is None:
+ print "Calculating UB matrix."
+ else:
+ print "Recalculating UB matrix."
+
+ if self._ROT is not None:
+ self._U = self._ROT * m * self._ROT.I
+ else:
+ self._U = m
+ self._state.configure_calc_type(manual_U=self._U)
+ if self._state.crystal is None:
+ raise DiffcalcException(
+ "A crystal must be specified before manually setting U")
+ self._UB = self._U * self._state.crystal.B
+ print ("NOTE: A new UB matrix will not be automatically calculated "
+ "when the orientation reflections are modified.")
+ self.save()
+
+ def set_UB_manually(self, m):
+ """Manually sets UB. matrix must be 3*3 Jama or python matrix.
+ Turns off aution UB calcualtion."""
+
+ # Check matrix is a 3*3 Jama matrix
+ if m.__class__ != matrix:
+ m = matrix(m) # assume its a python matrix
+ if m.shape[0] != 3 or m.shape[1] != 3:
+ raise ValueError("Expects 3*3 matrix")
+
+ if self._ROT is not None:
+ self._UB = self._ROT * m
+ else:
+ self._UB = m
+ self._state.configure_calc_type(manual_UB=self._UB)
+ self.save()
+
+ @property
+ def U(self):
+ if self._U is None:
+ raise DiffcalcException(
+ "No U matrix has been calculated during this ub calculation")
+ return self._U
+
+ @property
+ def UB(self):
+ return self._get_UB()
+
+ def is_ub_calculated(self):
+ return self._UB is not None
+
+ def _get_UB(self):
+ if not self.is_ub_calculated():
+ raise DiffcalcException(
+ "No UB matrix has been calculated during this ub calculation")
+ else:
+ return self._UB
+
+ def _calc_UB(self, h1, h2, u1p, u2p):
+ B = self._state.crystal.B
+ h1c = B * h1
+ h2c = B * h2
+
+ # Create modified unit vectors t1, t2 and t3 in crystal and phi systems
+ t1c = h1c
+ t3c = cross3(h1c, h2c)
+ t2c = cross3(t3c, t1c)
+
+ t1p = u1p # FIXED from h1c 9July08
+ t3p = cross3(u1p, u2p)
+ t2p = cross3(t3p, t1p)
+
+ # ...and nornmalise and check that the reflections used are appropriate
+ SMALL = 1e-4 # Taken from Vlieg's code
+ e = DiffcalcException("Invalid orientation reflection(s)")
+
+ def normalise(m):
+ d = norm(m)
+ if d < SMALL:
+ raise e
+ return m / d
+
+ t1c = normalise(t1c)
+ t2c = normalise(t2c)
+ t3c = normalise(t3c)
+
+ t1p = normalise(t1p)
+ t2p = normalise(t2p)
+ t3p = normalise(t3p)
+
+ Tc = hstack([t1c, t2c, t3c])
+ Tp = hstack([t1p, t2p, t3p])
+ self._state.configure_calc_type(or0=1, or1=2)
+ self._U = Tp * Tc.I
+ self._UB = self._U * B
+ self.save()
+
+ def calculate_UB(self):
+ """
+ Calculate orientation matrix. Uses first two orientation reflections
+ as in Busang and Levy, but for the diffractometer in Lohmeier and
+ Vlieg.
+ """
+
+ # Major variables:
+ # h1, h2: user input reciprical lattice vectors of the two reflections
+ # h1c, h2c: user input vectors in cartesian crystal plane
+ # pos1, pos2: measured diffractometer positions of the two reflections
+ # u1a, u2a: measured reflection vectors in alpha frame
+ # u1p, u2p: measured reflection vectors in phi frame
+
+
+ # Get hkl and angle values for the first two refelctions
+ if self._state.reflist is None:
+ raise DiffcalcException("Cannot calculate a U matrix until a "
+ "UBCalculation has been started with "
+ "'newub'")
+ try:
+ (h1, pos1, _, _, _) = self._state.reflist.getReflection(1)
+ (h2, pos2, _, _, _) = self._state.reflist.getReflection(2)
+ except IndexError:
+ raise DiffcalcException(
+ "Two reflections are required to calculate a U matrix")
+ h1 = matrix([h1]).T # row->column
+ h2 = matrix([h2]).T
+ pos1.changeToRadians()
+ pos2.changeToRadians()
+
+ # Compute the two reflections' reciprical lattice vectors in the
+ # cartesian crystal frame
+ u1p = self._strategy.calculate_q_phi(pos1)
+ u2p = self._strategy.calculate_q_phi(pos2)
+
+ self._calc_UB(h1, h2, u1p, u2p)
+
+ def calculate_UB_from_orientation(self):
+ """
+ Calculate orientation matrix. Uses first two crystal orientations.
+ """
+
+ # Major variables:
+ # h1, h2: user input reciprical lattice vectors of the two reflections
+ # h1c, h2c: user input vectors in cartesian crystal plane
+ # pos1, pos2: measured diffractometer positions of the two reflections
+ # u1a, u2a: measured reflection vectors in alpha frame
+ # u1p, u2p: measured reflection vectors in phi frame
+
+
+ # Get hkl and angle values for the first two crystal orientations
+ if self._state.orientlist is None:
+ raise DiffcalcException("Cannot calculate a U matrix until a "
+ "UBCalculation has been started with "
+ "'newub'")
+ try:
+ (h1, x1, _, _) = self._state.orientlist.getOrientation(1)
+ (h2, x2, _, _) = self._state.orientlist.getOrientation(2)
+ except IndexError:
+ raise DiffcalcException(
+ "Two crystal orientations are required to calculate a U matrix")
+ h1 = matrix([h1]).T # row->column
+ h2 = matrix([h2]).T
+ u1p = matrix([x1]).T
+ u2p = matrix([x2]).T
+
+ self._calc_UB(h1, h2, u1p, u2p)
+
+ def calculate_UB_from_primary_only(self):
+ """
+ Calculate orientation matrix with the shortest absolute angle change.
+ Uses first orientation reflection
+ """
+
+ # Algorithm from http://www.j3d.org/matrix_faq/matrfaq_latest.html
+
+ # Get hkl and angle values for the first two refelctions
+ if self._state.reflist is None:
+ raise DiffcalcException(
+ "Cannot calculate a u matrix until a UBCalcaluation has been "
+ "started with newub")
+ try:
+ (h, pos, _, _, _) = self._state.reflist.getReflection(1)
+ except IndexError:
+ raise DiffcalcException(
+ "One reflection is required to calculate a u matrix")
+
+ h = matrix([h]).T # row->column
+ pos.changeToRadians()
+ B = self._state.crystal.B
+ h_crystal = B * h
+ h_crystal = h_crystal * (1 / norm(h_crystal))
+
+ q_measured_phi = self._strategy.calculate_q_phi(pos)
+ q_measured_phi = q_measured_phi * (1 / norm(q_measured_phi))
+
+ rotation_axis = cross3(h_crystal, q_measured_phi)
+ rotation_axis = rotation_axis * (1 / norm(rotation_axis))
+
+ cos_rotation_angle = dot3(h_crystal, q_measured_phi)
+ rotation_angle = acos(cos_rotation_angle)
+
+ uvw = rotation_axis.T.tolist()[0] # TODO: cleanup
+ print "resulting U angle: %.5f deg" % (rotation_angle * TODEG)
+ u_repr = (', '.join(['% .5f' % el for el in uvw]))
+ print "resulting U axis direction: [%s]" % u_repr
+
+ u, v, w = uvw
+ rcos = cos(rotation_angle)
+ rsin = sin(rotation_angle)
+ m = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] # TODO: tidy
+ m[0][0] = rcos + u * u * (1 - rcos)
+ m[1][0] = w * rsin + v * u * (1 - rcos)
+ m[2][0] = -v * rsin + w * u * (1 - rcos)
+ m[0][1] = -w * rsin + u * v * (1 - rcos)
+ m[1][1] = rcos + v * v * (1 - rcos)
+ m[2][1] = u * rsin + w * v * (1 - rcos)
+ m[0][2] = v * rsin + u * w * (1 - rcos)
+ m[1][2] = -u * rsin + v * w * (1 - rcos)
+ m[2][2] = rcos + w * w * (1 - rcos)
+
+ if self._UB is None:
+ print "Calculating UB matrix from the first reflection only."
+ else:
+ print "Recalculating UB matrix from the first reflection only."
+ print ("NOTE: A new UB matrix will not be automatically calculated "
+ "when the orientation reflections are modified.")
+
+ self._state.configure_calc_type(or0=1)
+
+ self._U = matrix(m)
+ self._UB = self._U * B
+
+ self.save()
+
+ def set_miscut(self, xyz, angle, add_miscut=False):
+ """Calculate U matrix using a miscut axis and an angle"""
+ if xyz is None:
+ rot_matrix = xyz_rotation([0, 1, 0], angle)
+ if self.is_ub_calculated() and add_miscut:
+ self._U = rot_matrix * self._U
+ else:
+ self._U = rot_matrix
+ else:
+ rot_matrix = xyz_rotation(xyz, angle)
+ try:
+ rot_matrix = self._ROT * rot_matrix * self._ROT.I
+ except TypeError:
+ pass
+ if self.is_ub_calculated() and add_miscut:
+ self._U = rot_matrix * self._U
+ else:
+ self._U = rot_matrix
+ self._state.configure_calc_type(manual_U=self._U)
+ self._UB = self._U * self._state.crystal.B
+ self.print_reference()
+ self.save()
+
+ def get_hkl_plane_distance(self, hkl):
+ """Calculates and returns the distance between planes"""
+ return self._state.crystal.get_hkl_plane_distance(hkl)
+
+ def get_hkl_plane_angle(self, hkl1, hkl2):
+ """Calculates and returns the angle between planes"""
+ return self._state.crystal.get_hkl_plane_angle(hkl1, hkl2)
+
+ def rescale_unit_cell(self, h, k, l, pos):
+ """
+ Calculate unit cell scaling parameter that matches
+ given hkl position and diffractometer angles
+ """
+ q_vec = self._strategy.calculate_q_phi(pos)
+ q_hkl = norm(q_vec) / self._hardware.get_wavelength()
+ d_hkl = self._state.crystal.get_hkl_plane_distance([h, k, l])
+ sc = 1/ (q_hkl * d_hkl)
+ name, a1, a2, a3, alpha1, alpha2, alpha3 = self._state.crystal.getLattice()
+ if abs(sc - 1.) < SMALL:
+ return None, None
+ return sc, (name, sc * a1, sc* a2, sc * a3, alpha1, alpha2, alpha3)
+
+ def calc_miscut(self, h, k, l, pos):
+ """
+ Calculate miscut angle and axis that matches
+ given hkl position and diffractometer angles
+ """
+ q_vec = self._strategy.calculate_q_phi(pos)
+ hkl_nphi = self._UB * matrix([[h], [k], [l]])
+ try:
+ axis = cross3(self._ROT.I * q_vec, self._ROT.I * hkl_nphi)
+ except AttributeError:
+ axis = cross3(q_vec, hkl_nphi)
+ norm_axis = norm(axis)
+ if norm_axis < SMALL:
+ return None, None
+ axis = axis / norm(axis)
+ try:
+ miscut = acos(bound(dot3(q_vec, hkl_nphi) / (norm(q_vec) * norm(hkl_nphi)))) * TODEG
+ except AssertionError:
+ return None, None
+ return miscut, axis.T.tolist()[0]
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/ub/calcstate.py b/script/__Lib/diffcalc-2.1/diffcalc/ub/calcstate.py
new file mode 100755
index 0000000..1e8f438
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/ub/calcstate.py
@@ -0,0 +1,220 @@
+from diffcalc.hkl.vlieg.geometry import VliegPosition
+from diffcalc.ub.crystal import CrystalUnderTest
+from diffcalc.ub.reflections import ReflectionList, _Reflection
+from math import pi
+import datetime # @UnusedImport For crazy time eval code!
+from diffcalc.ub.reference import YouReference
+from diffcalc.ub.orientations import _Orientation, OrientationList
+
+try:
+ from collection import OrderedDict
+except ImportError:
+ from simplejson import OrderedDict
+
+try:
+ import json
+except ImportError:
+ import simplejson as json
+
+try:
+ from numpy import matrix
+except ImportError:
+ from numjy import matrix
+
+
+TODEG = 180 / pi
+
+
+class UBCalcState():
+
+ def __init__(self, name=None, crystal=None, reflist=None, orientlist=None, tau=0, sigma=0,
+ manual_U=None, manual_UB=None, or0=None, or1=None, reference=None):
+
+ assert reflist is not None
+ self.name = name
+ self.crystal = crystal
+ self.reflist = reflist
+ self.orientlist = orientlist
+ self.tau = tau # degrees
+ self.sigma = sigma # degrees
+ self.manual_U = manual_U
+ self.manual_UB = manual_UB
+ self.or0 = or0
+ self.or1 = or1
+ self.reference = reference
+
+ @property
+ def is_okay_to_autocalculate_ub(self):
+ nothing_set = ((self.manual_U is None) and
+ (self.manual_UB is None) and
+ (self.or0 is None) and
+ (self.or1 is None))
+ or0_and_or1_used = (self.or0 is not None) and (self.or1 is not None)
+ return nothing_set or or0_and_or1_used
+
+
+ def configure_calc_type(self,
+ manual_U=None,
+ manual_UB=None,
+ or0=None,
+ or1=None):
+ self.manual_U = manual_U
+ self.manual_UB = manual_UB
+ self.or0 = or0
+ self.or1 = or1
+
+
+class UBCalcStateEncoder(json.JSONEncoder):
+
+ def default(self, obj):
+
+ if isinstance(obj, UBCalcState):
+ d = OrderedDict()
+ d['name'] = obj.name
+ d['crystal'] = obj.crystal
+ d['reflist'] = obj.reflist
+ d['orientlist'] = obj.orientlist
+ d['tau'] = obj.tau
+ d['sigma'] = obj.sigma
+ d['reference'] = obj.reference
+ d['u'] = obj.manual_U
+ d['ub'] = obj.manual_UB
+ d['or0'] = obj.or0
+ d['or1'] = obj.or1
+
+ return d
+
+ if isinstance(obj, CrystalUnderTest):
+ return repr([obj._name, obj._a1, obj._a2, obj._a3, obj._alpha1 * TODEG,
+ obj._alpha2 * TODEG, obj._alpha3 * TODEG])
+
+ if isinstance(obj, matrix):
+ l = [', '.join((repr(e) for e in row)) for row in obj.tolist()]
+ return l
+
+ if isinstance(obj, ReflectionList):
+ d = OrderedDict()
+ for n, ref in enumerate(obj._reflist):
+ d[str(n+1)] = ref
+ return d
+
+ if isinstance(obj, _Reflection):
+ d = OrderedDict()
+ d['tag'] = obj.tag
+ d['hkl'] = repr([obj.h, obj.k, obj.l])
+ d['pos'] = repr(list(obj.pos.totuple()))
+ d['energy'] = obj.energy
+ dt = eval(obj.time) # e.g. --> datetime.datetime(2013, 8, 5, 15, 47, 7, 962432)
+ d['time'] = None if dt is None else dt.isoformat()
+ return d
+
+ if isinstance(obj, OrientationList):
+ d = OrderedDict()
+ for n, orient in enumerate(obj._orientlist):
+ d[str(n+1)] = orient
+ return d
+
+ if isinstance(obj, _Orientation):
+ d = OrderedDict()
+ d['tag'] = obj.tag
+ d['hkl'] = repr([obj.h, obj.k, obj.l])
+ d['xyz'] = repr([obj.x, obj.y, obj.z])
+ dt = eval(obj.time) # e.g. --> datetime.datetime(2013, 8, 5, 15, 47, 7, 962432)
+ d['time'] = None if dt is None else dt.isoformat()
+ return d
+
+ if isinstance(obj, YouReference):
+ d = OrderedDict()
+ if obj.n_hkl_configured is not None:
+ d['n_hkl_configured'] = repr(obj.n_hkl_configured.T.tolist()[0])
+ else:
+ d['n_hkl_configured'] = None
+ if obj.n_phi_configured is not None:
+ d['n_phi_configured'] = repr(obj.n_phi_configured.T.tolist()[0])
+ else:
+ d['n_phi_configured'] = None
+ return d
+
+
+ return json.JSONEncoder.default(self, obj)
+
+
+def decode_ubcalcstate(state, geometry, diffractometer_axes_names):
+
+ # Backwards compatibility code
+ orientlist_=OrientationList([])
+ try:
+ orientlist_=decode_orientlist(state['orientlist'])
+ except KeyError:
+ pass
+ return UBCalcState(
+ name=state['name'],
+ crystal=state['crystal'] and CrystalUnderTest(*eval(state['crystal'])),
+ reflist=decode_reflist(state['reflist'], geometry, diffractometer_axes_names),
+ orientlist=orientlist_,
+ tau=state['tau'],
+ sigma=state['sigma'],
+ manual_U=state['u'] and decode_matrix(state['u']),
+ manual_UB=state['ub'] and decode_matrix(state['ub']),
+ or0=state['or0'],
+ or1=state['or1'],
+ reference=decode_reference(state.get('reference', None))
+ )
+
+
+def decode_matrix(rows):
+ return matrix([[eval(e) for e in row.split(', ')] for row in rows])
+
+
+def decode_reflist(reflist_dict, geometry, diffractometer_axes_names):
+ reflections = []
+ for key in sorted(reflist_dict.keys()):
+ reflections.append(decode_reflection(reflist_dict[key], geometry))
+
+ return ReflectionList(geometry, diffractometer_axes_names, reflections)
+
+
+def decode_orientlist(orientlist_dict):
+ orientations = []
+ for key in sorted(orientlist_dict.keys()):
+ orientations.append(decode_orientation(orientlist_dict[key]))
+
+ return OrientationList(orientations)
+
+
+def decode_reflection(ref_dict, geometry):
+ h, k, l = eval(ref_dict['hkl'])
+ time = ref_dict['time'] and gt(ref_dict['time'])
+ pos_tuple = eval(ref_dict['pos'])
+ try:
+ position = geometry.create_position(*pos_tuple)
+ except AttributeError:
+ position = VliegPosition(*pos_tuple)
+ return _Reflection(h, k, l, position, ref_dict['energy'], str(ref_dict['tag']), repr(time))
+
+
+def decode_reference(ref_dict):
+ reference = YouReference(None) # TODO: We can't set get_ub method yet (tangles!)
+ if ref_dict:
+ nhkl = ref_dict.get('n_hkl_configured', None)
+ nphi = ref_dict.get('n_phi_configured', None)
+ if nhkl:
+ reference.n_hkl_configured = matrix([eval(nhkl)]).T
+ if nphi:
+ reference.n_phi_configured = matrix([eval(nphi)]).T
+ return reference
+
+
+def decode_orientation(orient_dict):
+ h, k, l = eval(orient_dict['hkl'])
+ x, y, z = eval(orient_dict['xyz'])
+ time = orient_dict['time'] and gt(orient_dict['time'])
+ return _Orientation(h, k, l, x, y, z, str(orient_dict['tag']), repr(time))
+
+
+# From: http://stackoverflow.com/questions/127803/how-to-parse-iso-formatted-date-in-python
+def gt(dt_str):
+ dt, _, us= dt_str.partition(".")
+ dt= datetime.datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S")
+ us= int(us.rstrip("Z"), 10)
+ return dt + datetime.timedelta(microseconds=us)
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/ub/crystal.py b/script/__Lib/diffcalc-2.1/diffcalc/ub/crystal.py
new file mode 100755
index 0000000..84072d9
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/ub/crystal.py
@@ -0,0 +1,147 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from math import pi, cos, sin, acos, sqrt
+from diffcalc.util import angle_between_vectors
+
+
+try:
+ from numpy import matrix
+except ImportError:
+ from numjy import matrix
+
+TORAD = pi / 180
+TODEG = 180 / pi
+SMALL = 1e-7
+
+def z(num):
+ """Round to zero if small. This is useful to get rid of erroneous
+ minus signs resulting from float representation close to zero.
+ """
+ if abs(num) < SMALL:
+ num = 0
+ return num
+
+
+class CrystalUnderTest(object):
+ """
+ Contains the lattice parameters and calculated B matrix for the crytsal
+ under test. Also Calculates the distance between planes at a given hkl
+ value.
+
+ The lattice paraemters can be specified and then if desired saved to a
+ __library to be loaded later. The parameters are persisted across restarts.
+ Lattices stored in config/var/crystals.xml .
+ """
+
+ def __init__(self, name, a, b, c, alpha, beta, gamma):
+ '''Creates a new lattice and calculates related values.
+
+ Keyword arguments:
+ name -- a string
+ a,b,c,alpha,beta,gamma -- lengths and angles (in degrees)
+ '''
+
+ self._name = name
+
+ # Set the direct lattice parameters
+ self._a1 = a1 = a
+ self._a2 = a2 = b
+ self._a3 = a3 = c
+ self._alpha1 = alpha1 = alpha * TORAD
+ self._alpha2 = alpha2 = beta * TORAD
+ self._alpha3 = alpha3 = gamma * TORAD
+
+ # Calculate the reciprocal lattice parameters
+ self._beta1 = acos((cos(alpha2) * cos(alpha3) - cos(alpha1)) /
+ (sin(alpha2) * sin(alpha3)))
+
+ self._beta2 = beta2 = acos((cos(alpha1) * cos(alpha3) - cos(alpha2)) /
+ (sin(alpha1) * sin(alpha3)))
+
+ self._beta3 = beta3 = acos((cos(alpha1) * cos(alpha2) - cos(alpha3)) /
+ (sin(alpha1) * sin(alpha2)))
+
+ volume = (a1 * a2 * a3 *
+ sqrt(1 + 2 * cos(alpha1) * cos(alpha2) * cos(alpha3) -
+ cos(alpha1) ** 2 - cos(alpha2) ** 2 - cos(alpha3) ** 2))
+
+ self._b1 = b1 = 2 * pi * a2 * a3 * sin(alpha1) / volume
+ self._b2 = b2 = 2 * pi * a1 * a3 * sin(alpha2) / volume
+ self._b3 = b3 = 2 * pi * a1 * a2 * sin(alpha3) / volume
+
+ # Calculate the BMatrix from the direct and reciprical parameters.
+ # Reference: Busang and Levy (1967)
+ self._bMatrix = matrix([
+ [b1, b2 * cos(beta3), b3 * cos(beta2)],
+ [0.0, b2 * sin(beta3), -b3 * sin(beta2) * cos(alpha1)],
+ [0.0, 0.0, 2 * pi / a3]])
+
+ @property
+ def B(self):
+ '''
+ Returns the B matrix, may be null if crystal is not set, or if there
+ was a problem calculating this'''
+ return self._bMatrix
+
+ def get_hkl_plane_distance(self, hkl):
+ '''Calculates and returns the distance between planes'''
+ hkl = matrix([hkl])
+ bReduced = self._bMatrix / (2 * pi)
+ bMT = bReduced.I * bReduced.T.I
+ return 1.0 / sqrt((hkl * bMT.I * hkl.T)[0,0])
+
+ def get_hkl_plane_angle(self, hkl1, hkl2):
+ '''Calculates and returns the angle between [hkl1] and [hkl2] planes'''
+ hkl1 = matrix([hkl1]).T
+ hkl2 = matrix([hkl2]).T
+ nphi1 = self._bMatrix * hkl1
+ nphi2 = self._bMatrix * hkl2
+ angle = angle_between_vectors(nphi1, nphi2)
+ return angle
+
+ def __str__(self):
+ ''' Returns lattice name and all set and calculated parameters'''
+ return '\n'.join(self.str_lines())
+
+ def str_lines(self):
+ WIDTH = 13
+ if self._name is None:
+ return [" none specified"]
+
+ b = self._bMatrix
+ lines = []
+ lines.append(" name:".ljust(WIDTH) + self._name.rjust(9))
+ lines.append("")
+ lines.append(" a, b, c:".ljust(WIDTH) +
+ "% 9.5f % 9.5f % 9.5f" % (self.getLattice()[1:4]))
+ lines.append(" " * WIDTH +
+ "% 9.5f % 9.5f % 9.5f" % (self.getLattice()[4:]))
+ lines.append("")
+
+ fmt = "% 9.5f % 9.5f % 9.5f"
+ lines.append(" B matrix:".ljust(WIDTH) +
+ fmt % (z(b[0, 0]), z(b[0, 1]), z(b[0, 2])))
+ lines.append(' ' * WIDTH + fmt % (z(b[1, 0]), z(b[1, 1]), z(b[1, 2])))
+ lines.append(' ' * WIDTH + fmt % (z(b[2, 0]), z(b[2, 1]), z(b[2, 2])))
+ return lines
+
+ def getLattice(self):
+ return(self._name, self._a1, self._a2, self._a3, self._alpha1 * TODEG,
+ self._alpha2 * TODEG, self._alpha3 * TODEG)
+
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/ub/orientations.py b/script/__Lib/diffcalc-2.1/diffcalc/ub/orientations.py
new file mode 100755
index 0000000..1726136
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/ub/orientations.py
@@ -0,0 +1,118 @@
+###
+# Copyright 2008-2017 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from copy import deepcopy
+import datetime # @UnusedImport for the eval below
+
+try:
+ from numpy import matrix
+except ImportError:
+ from numjy import matrix
+
+from diffcalc.util import DiffcalcException, bold
+
+
+class _Orientation:
+ """A orientation"""
+ def __init__(self, h, k, l, x, y, z, tag, time):
+
+ self.h = float(h)
+ self.k = float(k)
+ self.l = float(l)
+ self.x = float(x)
+ self.y = float(y)
+ self.z = float(z)
+ self.tag = tag
+ self.time = time # Saved as e.g. repr(datetime.now())
+
+ def __str__(self):
+ return ("h=%-4.2f k=%-4.2f l=%-4.2f x=%-4.2f "
+ "y=%-4.2f z=%-4.2 "
+ " %-s %s" % (self.h, self.k, self.l,
+ self.x, self.y, self.z,
+ self.tag, self.time))
+
+
+class OrientationList:
+
+ def __init__(self, orientations=None):
+ self._orientlist = orientations if orientations else []
+
+
+ def add_orientation(self, h, k, l, x, y, z, tag, time):
+ """adds a crystal orientation
+ """
+ self._orientlist += [_Orientation(h, k, l, x, y, z, tag,
+ time.__repr__())]
+
+ def edit_orientation(self, num, h, k, l, x, y, z, tag, time):
+ """num starts at 1"""
+ try:
+ self._orientlist[num - 1] = _Orientation(h, k, l, x, y, z, tag,
+ time.__repr__())
+ except IndexError:
+ raise DiffcalcException("There is no orientation " + repr(num)
+ + " to edit.")
+
+ def getOrientation(self, num):
+ """
+ getOrientation(num) --> ( [h, k, l], [x, y, z], tag, time ) --
+ num starts at 1
+ """
+ r = deepcopy(self._orientlist[num - 1]) # for convenience
+ return [r.h, r.k, r.l], [r.x, r.y, r.z], r.tag, eval(r.time)
+
+ def removeOrientation(self, num):
+ del self._orientlist[num - 1]
+
+ def swap_orientations(self, num1, num2):
+ orig1 = self._orientlist[num1 - 1]
+ self._orientlist[num1 - 1] = self._orientlist[num2 - 1]
+ self._orientlist[num2 - 1] = orig1
+
+ def __len__(self):
+ return len(self._orientlist)
+
+ def __str__(self):
+ return '\n'.join(self.str_lines())
+
+ def str_lines(self, R=None):
+ if not self._orientlist:
+ return [" <<< none specified >>>"]
+
+ lines = []
+
+ str_format = (" %5s %5s %5s %5s %5s %5s TAG")
+ values = ('H', 'K', 'L', 'X', 'Y', 'Z')
+ lines.append(bold(str_format % values))
+
+ for n in range(len(self._orientlist)):
+ orient_tuple = self.getOrientation(n + 1)
+ [h, k, l], [x, y, z], tag, _ = orient_tuple
+ try:
+ xyz_rot = R.I * matrix([[x],[y],[z]])
+ xr, yr, zr = xyz_rot.T.tolist()[0]
+ except AttributeError:
+ xr, yr, zr = x ,y ,z
+ if tag is None:
+ tag = ""
+ str_format = (" %2d % 4.2f % 4.2f % 4.2f " +
+ "% 4.2f % 4.2f % 4.2f %s")
+ values = (n + 1, h, k, l, xr, yr, zr, tag)
+ lines.append(str_format % values)
+ return lines
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/ub/persistence.py b/script/__Lib/diffcalc-2.1/diffcalc/ub/persistence.py
new file mode 100755
index 0000000..526dbcd
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/ub/persistence.py
@@ -0,0 +1,151 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from __future__ import with_statement
+
+import os, glob
+from diffcalc.ub.calcstate import UBCalcStateEncoder
+import datetime
+
+try:
+ import json
+except ImportError:
+ import simplejson as json
+
+
+def is_writable(directory):
+ """Return true if the file is writable from the current user
+ """
+ probe = os.path.join(directory, "probe")
+ try:
+ open(probe, 'w')
+ except IOError:
+ return False
+ else:
+ os.remove(probe)
+ return True
+
+def check_directory_appropriate(directory):
+
+ if not os.path.exists(directory):
+ raise IOError("'%s' does not exist")
+
+ if not os.path.isdir(directory):
+ raise IOError("'%s' is not a directory")
+
+ if not is_writable(directory):
+ raise IOError("'%s' is not writable")
+
+
+class UBCalculationJSONPersister(object):
+
+ def __init__(self, directory):
+ check_directory_appropriate(directory)
+ self.directory = directory
+ self.description = directory
+
+ def filepath(self, name):
+ return os.path.join(self.directory, name + '.json')
+
+ def save(self, state, name):
+ # FORMAT = '%Y-%m-%d %H:%M:%S'
+ # time_string = datetime.datetime.strftime(datetime.datetime.now(), FORMAT)
+ with open(self.filepath(name), 'w') as f:
+ json.dump(state, f, indent=4, cls=UBCalcStateEncoder)
+
+ def load(self, name):
+ with open(self.filepath(name), 'r') as f:
+ return json.load(f)
+
+ def list(self): # @ReservedAssignment
+ files = self._get_save_files()
+ return [os.path.basename(f + '.json').split('.json')[0] for f in files]
+
+ def list_metadata(self):
+ metadata = []
+ for f in self._get_save_files():
+ dt = datetime.datetime.fromtimestamp(os.path.getmtime(f))
+ metadata.append(dt.strftime('%d %b %Y (%H:%M)'))
+ return metadata
+
+ def _get_save_files(self):
+ files = filter(os.path.isfile, glob.glob(os.path.join(self.directory, '*.json')))
+ files.sort(key=lambda x: os.path.getmtime(x))
+ files.reverse()
+ return files
+
+ def remove(self, name):
+ os.remove(self.filepath(name))
+
+
+
+class UBCalculationPersister(object):
+ """Attempts to the use the gda's database to store ub calculation state
+ """
+ def __init__(self):
+ try:
+ from uk.ac.diamond.daq.persistence.jythonshelf import LocalJythonShelfManager
+ from uk.ac.diamond.daq.persistence.jythonshelf.LocalDatabase import \
+ LocalDatabaseException
+ self.shelf = LocalJythonShelfManager.getLocalObjectShelf(
+ 'diffcalc.ub')
+ except ImportError, e:
+ print ("!!! UBCalculationPersister could not import the gda database "
+ "code: " + repr(e))
+ self.shelf = None
+ except LocalDatabaseException, e:
+ print ("UBCalculationPersister could not connect to the gda "
+ "database: " + repr(e))
+ self.shelf = None
+ self.description = 'GDA sql database'
+
+ def save(self, state, key):
+ if self.shelf is not None:
+ self.shelf[key] = state
+ else:
+ print "<<>>"
+
+ def load(self, name):
+ if self.shelf is not None:
+ return self.shelf[name]
+ else:
+ raise IOError("Could not load UB calculation: no database available")
+
+ def list(self): # @ReservedAssignment
+ if self.shelf is not None:
+ names = list(self.shelf.keys())
+ names.sort()
+ return names
+ else:
+ return []
+
+ def remove(self, name):
+ if self.shelf is not None:
+ del self.shelf[name]
+ else:
+ raise IOError("Could not remove UB calculation: no database available")
+
+
+class UbCalculationNonPersister(UBCalculationPersister):
+ """
+ A version of UBCalculationPersister that simply stores to a local dict
+ rather than a database. Useful for testing.
+ """
+ def __init__(self):
+ self.shelf = dict()
+ self.description = 'memory only'
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/ub/reference.py b/script/__Lib/diffcalc-2.1/diffcalc/ub/reference.py
new file mode 100755
index 0000000..53a8e4b
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/ub/reference.py
@@ -0,0 +1,99 @@
+from math import pi, acos
+
+try:
+ from numpy import matrix
+ from numpy.linalg import norm
+except ImportError:
+ from numjy import matrix
+ from numjy.linalg import norm
+
+from diffcalc.util import cross3, dot3
+
+
+SMALL = 1e-7
+TODEG = 180 / pi
+
+class YouReference(object):
+
+ def __init__(self, get_UB):
+ self.get_UB = get_UB # callable
+ self._n_phi_configured = None
+ self._n_hkl_configured = None
+ self._set_n_phi_configured(matrix('0; 0; 1'))
+
+ def _set_n_phi_configured(self, n_phi):
+ self._n_phi_configured = n_phi
+ self._n_hkl_configured = None
+
+ def _get_n_phi_configured(self):
+ return self._n_phi_configured
+
+ n_phi_configured = property(_get_n_phi_configured, _set_n_phi_configured)
+
+ def _set_n_hkl_configured(self, n_hkl):
+ self._n_phi_configured = None
+ self._n_hkl_configured = n_hkl
+
+ def _get_n_hkl_configured(self):
+ return self._n_hkl_configured
+
+ n_hkl_configured = property(_get_n_hkl_configured, _set_n_hkl_configured)
+
+ @property
+ def n_phi(self):
+ n_phi = (self.get_UB() * self._n_hkl_configured if self._n_phi_configured is None
+ else self._n_phi_configured)
+ return n_phi / norm(n_phi)
+
+ @property
+ def n_hkl(self):
+ n_hkl = (self.get_UB().I * self._n_phi_configured if self._n_hkl_configured is None
+ else self._n_hkl_configured)
+ return n_hkl / norm(n_hkl)
+
+ def _pretty_vector(self, m):
+ return ' '.join([('% 9.5f' % e).rjust(9) for e in m.T.tolist()[0]])
+
+ def repr_lines(self, ub_calculated, WIDTH=9, R=None):
+ SET_LABEL = ' <- set'
+ lines = []
+ if self._n_phi_configured is not None:
+ nphi_label = SET_LABEL
+ nhkl_label = ''
+ elif self._n_hkl_configured is not None:
+ nphi_label = ''
+ nhkl_label = SET_LABEL
+ else:
+ raise AssertionError("Neither a manual n_phi nor n_hkl is configured")
+
+ if ub_calculated:
+ try:
+ lines.append(" n_phi:".ljust(WIDTH) + self._pretty_vector(R.I * self.n_phi) + nphi_label)
+ except AttributeError:
+ lines.append(" n_phi:".ljust(WIDTH) + self._pretty_vector(self.n_phi) + nphi_label)
+ lines.append(" n_hkl:".ljust(WIDTH) + self._pretty_vector(self.n_hkl) + nhkl_label)
+ try:
+ rotation_axis = R.I * cross3(matrix('0; 0; 1'), self.n_phi)
+ except AttributeError:
+ rotation_axis = cross3(matrix('0; 0; 1'), self.n_phi)
+ if abs(norm(rotation_axis)) < SMALL:
+ lines.append(" normal:".ljust(WIDTH) + " None")
+ else:
+ rotation_axis = rotation_axis * (1 / norm(rotation_axis))
+ cos_rotation_angle = dot3(matrix('0; 0; 1'), self.n_phi)
+ rotation_angle = acos(cos_rotation_angle)
+ lines.append(" normal:")
+ lines.append(" angle:".ljust(WIDTH) + "% 9.5f" % (rotation_angle * TODEG))
+ lines.append(" axis:".ljust(WIDTH) + self._pretty_vector(rotation_axis))
+
+ else: # no ub calculated
+ if self._n_phi_configured is not None:
+ try:
+ lines.append(" n_phi:".ljust(WIDTH) + self._pretty_vector(R.I * self._n_phi_configured) + SET_LABEL)
+ except AttributeError:
+ lines.append(" n_phi:".ljust(WIDTH) + self._pretty_vector(self._n_phi_configured) + SET_LABEL)
+ elif self._n_hkl_configured is not None:
+ lines.append(" n_hkl:".ljust(WIDTH) + self._pretty_vector(self._n_hkl_configured) + SET_LABEL)
+
+ return lines
+
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/ub/reflections.py b/script/__Lib/diffcalc-2.1/diffcalc/ub/reflections.py
new file mode 100755
index 0000000..c447468
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/ub/reflections.py
@@ -0,0 +1,126 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from copy import deepcopy
+import datetime # @UnusedImport for the eval below
+from diffcalc.util import DiffcalcException, bold
+from diffcalc.hkl.vlieg.geometry import VliegPosition
+
+
+class _Reflection:
+ """A reflection"""
+ def __init__(self, h, k, l, position, energy, tag, time):
+
+ self.h = float(h)
+ self.k = float(k)
+ self.l = float(l)
+ self.pos = position
+ self.tag = tag
+ self.energy = float(energy) # energy=12.39842/lambda
+ self.wavelength = 12.3984 / self.energy
+ self.time = time # Saved as e.g. repr(datetime.now())
+
+ def __str__(self):
+ return ("energy=%-6.3f h=%-4.2f k=%-4.2f l=%-4.2f alpha=%-8.4f "
+ "delta=%-8.4f gamma=%-8.4f omega=%-8.4f chi=%-8.4f "
+ "phi=%-8.4f %-s %s" % (self.energy, self.h, self.k, self.l,
+ self.pos.alpha, self.pos.delta, self.pos.gamma, self.pos.omega,
+ self.pos.chi, self.pos.phi, self.tag, self.time))
+
+
+class ReflectionList:
+
+ def __init__(self, diffractometerPluginObject, externalAngleNames, reflections=None):
+ self._geometry = diffractometerPluginObject
+ self._externalAngleNames = externalAngleNames
+ self._reflist = reflections if reflections else []
+
+
+ def add_reflection(self, h, k, l, position, energy, tag, time):
+ """adds a reflection, position in degrees
+ """
+ if type(position) in (list, tuple):
+ try:
+ position = self._geometry.create_position(*position)
+ except AttributeError:
+ position = VliegPosition(*position)
+ self._reflist += [_Reflection(h, k, l, position, energy, tag,
+ time.__repr__())]
+
+ def edit_reflection(self, num, h, k, l, position, energy, tag, time):
+ """num starts at 1"""
+ if type(position) in (list, tuple):
+ position = VliegPosition(*position)
+ try:
+ self._reflist[num - 1] = _Reflection(h, k, l, position, energy, tag,
+ time.__repr__())
+ except IndexError:
+ raise DiffcalcException("There is no reflection " + repr(num)
+ + " to edit.")
+
+ def getReflection(self, num):
+ """
+ getReflection(num) --> ( [h, k, l], position, energy, tag, time ) --
+ num starts at 1 position in degrees
+ """
+ r = deepcopy(self._reflist[num - 1]) # for convenience
+ return [r.h, r.k, r.l], deepcopy(r.pos), r.energy, r.tag, eval(r.time)
+
+ def get_reflection_in_external_angles(self, num):
+ """getReflection(num) --> ( [h, k, l], (angle1...angleN), energy, tag )
+ -- num starts at 1 position in degrees"""
+ r = deepcopy(self._reflist[num - 1]) # for convenience
+ externalAngles = self._geometry.internal_position_to_physical_angles(r.pos)
+ result = [r.h, r.k, r.l], externalAngles, r.energy, r.tag, eval(r.time)
+ return result
+
+ def removeReflection(self, num):
+ del self._reflist[num - 1]
+
+ def swap_reflections(self, num1, num2):
+ orig1 = self._reflist[num1 - 1]
+ self._reflist[num1 - 1] = self._reflist[num2 - 1]
+ self._reflist[num2 - 1] = orig1
+
+ def __len__(self):
+ return len(self._reflist)
+
+ def __str__(self):
+ return '\n'.join(self.str_lines())
+
+ def str_lines(self):
+ axes = tuple(s.upper() for s in self._externalAngleNames)
+ if not self._reflist:
+ return [" <<< none specified >>>"]
+
+ lines = []
+
+ format = (" %6s %5s %5s %5s " + "%8s " * len(axes) + " TAG")
+ values = ('ENERGY', 'H', 'K', 'L') + axes
+ lines.append(bold(format % values))
+
+ for n in range(len(self._reflist)):
+ ref_tuple = self.get_reflection_in_external_angles(n + 1)
+ [h, k, l], externalAngles, energy, tag, _ = ref_tuple
+ if tag is None:
+ tag = ""
+ format = (" %2d %6.3f % 4.2f % 4.2f % 4.2f " +
+ "% 8.4f " * len(axes) + " %s")
+ values = (n + 1, energy, h, k, l) + externalAngles + (tag,)
+ lines.append(format % values)
+ return lines
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/ub/ub.py b/script/__Lib/diffcalc-2.1/diffcalc/ub/ub.py
new file mode 100755
index 0000000..afd2f47
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/ub/ub.py
@@ -0,0 +1,768 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from diffcalc import settings
+from diffcalc.ub.calc import UBCalculation
+
+from math import asin, pi
+from datetime import datetime
+
+try:
+ from numpy import matrix
+except ImportError:
+ from numjy import matrix
+
+
+from diffcalc.util import getInputWithDefault as promptForInput, \
+ promptForNumber, promptForList, allnum, isnum, bold, xyz_rotation
+from diffcalc.util import command
+
+TORAD = pi / 180
+TODEG = 180 / pi
+
+
+# When using ipython magic, these functions must not be imported to the top
+# level namespace. Doing so will stop them from being called with magic.
+
+__all__ = ['addorient', 'addref', 'c2th', 'hklangle', 'calcub', 'delorient', 'delref', 'editorient',
+ 'editref', 'listub', 'loadub', 'newub', 'orientub', 'saveubas', 'setlat',
+ 'addmiscut', 'setmiscut', 'setu', 'setub', 'showorient', 'showref', 'swaporient',
+ 'swapref', 'trialub', 'checkub', 'ub', 'ubcalc', 'rmub', 'clearorient',
+ 'clearref', 'lastub', 'refineub']
+
+if settings.include_sigtau:
+ __all__.append('sigtau')
+
+if settings.include_reference:
+ __all__.append('setnphi')
+ __all__.append('setnhkl')
+
+
+ubcalc = UBCalculation(settings.hardware,
+ settings.geometry,
+ settings.ubcalc_persister,
+ settings.ubcalc_strategy,
+ settings.include_sigtau,
+ settings.include_reference)
+
+
+
+### UB state ###
+
+@command
+def newub(name=None):
+ """newub {'name'} -- start a new ub calculation name
+ """
+ if name is None:
+ # interactive
+ name = promptForInput('calculation name')
+ ubcalc.start_new(name)
+ setlat()
+ elif isinstance(name, str):
+ # just trying might cause confusion here
+ ubcalc.start_new(name)
+ else:
+ raise TypeError()
+
+@command
+def loadub(name_or_num):
+ """loadub 'name' | num -- load an existing ub calculation
+ """
+ if isinstance(name_or_num, str):
+ ubcalc.load(name_or_num)
+ else:
+ ubcalc.load(ubcalc.listub()[int(name_or_num)])
+
+@command
+def lastub():
+ """lastub -- load the last used ub calculation
+ """
+ try:
+ lastub_name = ubcalc.listub()[0]
+ print "Loading ub calculation: '%s'" % lastub_name
+ loadub(0)
+ except IndexError:
+ print "WARNING: There is no record of the last ub calculation used"
+
+@command
+def rmub(name_or_num):
+ """rmub 'name'|num -- remove existing ub calculation
+ """
+ if isinstance(name_or_num, str):
+ ubcalc.remove(name_or_num)
+ else:
+ ubcalc.remove(ubcalc.listub()[int(name_or_num)])
+
+@command
+def listub():
+ """listub -- list the ub calculations available to load.
+ """
+ if hasattr(ubcalc._persister, 'description'):
+ print "UB calculations in: " + ubcalc._persister.description
+ else:
+ print "UB calculations:"
+ print
+ ubnames = ubcalc.listub()
+ # TODO: whole mechanism of making two calls is messy
+ try:
+ ub_metadata = ubcalc.listub_metadata()
+ except AttributeError:
+ ub_metadata = [''] * len(ubnames)
+
+ for n, name, data in zip(range(len(ubnames)), ubnames, ub_metadata):
+ print "%2i) %-15s %s" % (n, name, data)
+
+@command
+def saveubas(name):
+ """saveubas 'name' -- save the ub calculation with a new name
+ """
+ if isinstance(name, str):
+ # just trying might cause confusion here
+ ubcalc.saveas(name)
+ else:
+ raise TypeError()
+
+@command
+def ub():
+ """ub -- show the complete state of the ub calculation
+ """
+ #wavelength = float(hardware.get_wavelength())
+ #energy = float(hardware.get_energy())
+ print ubcalc.__str__()
+
+@command
+def refineub(*args):
+ """
+ refineub {[h k l]} {pos} -- refine unit cell dimensions and U matrix to match diffractometer angles for a given hkl value
+ """
+ if len(args) > 0:
+ args = list(args)
+ h, k, l = args.pop(0)
+ if not (isnum(h) and isnum(k) and isnum(l)):
+ raise TypeError()
+ else:
+ h = promptForNumber('h', 0.)
+ k = promptForNumber('k', 0.)
+ l = promptForNumber('l', 0.)
+ if None in (h, k, l):
+ _handleInputError("h,k and l must all be numbers")
+ if len(args) == 1:
+ pos = settings.geometry.physical_angles_to_internal_position( # @UndefinedVariable
+ args.pop(0))
+ elif len(args) == 0:
+ reply = promptForInput('current pos', 'y')
+ if reply in ('y', 'Y', 'yes'):
+ positionList = settings.hardware.get_position() # @UndefinedVariable
+ else:
+ currentPos = settings.hardware.get_position() # @UndefinedVariable
+ positionList = []
+ names = settings.hardware.get_axes_names() # @UndefinedVariable
+ for i, angleName in enumerate(names):
+ val = promptForNumber(angleName.rjust(7), currentPos[i])
+ if val is None:
+ _handleInputError("Please enter a number, or press"
+ " Return to accept default!")
+ return
+ positionList.append(val)
+ pos = settings.geometry.physical_angles_to_internal_position(positionList) # @UndefinedVariable
+ else:
+ raise TypeError()
+
+ pos.changeToRadians()
+ scale, lat = ubcalc.rescale_unit_cell(h, k, l, pos)
+ if scale:
+ lines = ["Unit cell scaling factor:".ljust(9) +
+ "% 9.5f" % scale]
+ lines.append("Refined crystal lattice:")
+ lines.append(" a, b, c:".ljust(9) +
+ "% 9.5f % 9.5f % 9.5f" % (lat[1:4]))
+ lines.append(" " * 12 +
+ "% 9.5f % 9.5f % 9.5f" % (lat[4:]))
+ lines.append("")
+ print '\n'.join(lines)
+ reply = promptForInput('Update crystal settings?', 'y')
+ if reply in ('y', 'Y', 'yes'):
+ ubcalc.set_lattice(*lat)
+ else:
+ print "No unit cell mismatch detected"
+ mc_angle, mc_axis = ubcalc.calc_miscut(h, k, l, pos)
+ if mc_angle:
+ lines = ["Miscut parameters:",]
+ lines.append(" angle:".ljust(9) + "% 9.5f" % mc_angle)
+ lines.append(" axis:".ljust(9) + "% 9.5f % 9.5f % 9.5f" % tuple(mc_axis))
+ print '\n'.join(lines)
+ reply = promptForInput('Apply miscut parameters?', 'y')
+ if reply in ('y', 'Y', 'yes'):
+ ubcalc.set_miscut(mc_axis, -mc_angle * TORAD, True)
+ else:
+ print "No miscut detected for the given settings"
+
+### UB lattice ###
+
+@command
+def setlat(name=None, *args):
+ """
+ setlat -- interactively enter lattice parameters (Angstroms and Deg)
+ setlat name a -- assumes cubic
+ setlat name a b -- assumes tetragonal
+ setlat name a b c -- assumes ortho
+ setlat name a b c gamma -- assumes mon/hex with gam not equal to 90
+ setlat name a b c alpha beta gamma -- arbitrary
+ """
+
+ if name is None: # Interactive
+ name = promptForInput("crystal name")
+ a = promptForNumber(' a', 1)
+ b = promptForNumber(' b', a)
+ c = promptForNumber(' c', a)
+ alpha = promptForNumber('alpha', 90)
+ beta = promptForNumber('beta', 90)
+ gamma = promptForNumber('gamma', 90)
+ ubcalc.set_lattice(name, a, b, c, alpha, beta, gamma)
+
+ elif (isinstance(name, str) and
+ len(args) in (1, 2, 3, 4, 6) and
+ allnum(args)):
+ # first arg is string and rest are numbers
+ ubcalc.set_lattice(name, *args)
+ else:
+ raise TypeError()
+
+@command
+def c2th(hkl, en=None):
+ """
+ c2th [h k l] -- calculate two-theta angle for reflection
+ """
+ if en is None:
+ wl = settings.hardware.get_wavelength() # @UndefinedVariable
+ else:
+ wl = 12.39842 / en
+ d = ubcalc.get_hkl_plane_distance(hkl)
+ if wl > (2 * d):
+ raise ValueError(
+ 'Reflection un-reachable as wavelength (%f) is more than twice\n'
+ 'the plane distance (%f)' % (wl, d))
+ try:
+ return 2.0 * asin(wl / (d * 2)) * TODEG
+ except ValueError as e:
+ raise ValueError('asin(wl / (d * 2) with wl=%f and d=%f: ' %(wl, d) + e.args[0])
+
+
+@command
+def hklangle(hkl1, hkl2):
+ """
+ hklangle [h1 k1 l1] [h2 k2 l2] -- calculate angle between [h1 k1 l1] and [h2 k2 l2] planes
+ """
+ return ubcalc.get_hkl_plane_angle(hkl1, hkl2) * TODEG
+
+### Surface and reference vector stuff ###
+
+@command
+def sigtau(sigma=None, tau=None):
+ """sigtau {sigma tau} -- sets or displays sigma and tau"""
+ if sigma is None and tau is None:
+ chi = settings.hardware.get_position_by_name('chi') # @UndefinedVariable
+ phi = settings.hardware.get_position_by_name('phi') # @UndefinedVariable
+ _sigma, _tau = ubcalc.sigma, ubcalc.tau
+ print "sigma, tau = %f, %f" % (_sigma, _tau)
+ print " chi, phi = %f, %f" % (chi, phi)
+ sigma = promptForInput("sigma", -chi)
+ tau = promptForInput(" tau", -phi)
+ ubcalc.sigma = sigma
+ ubcalc.tau = tau
+ else:
+ ubcalc.sigma = float(sigma)
+ ubcalc.tau = float(tau)
+
+
+@command
+def setnphi(xyz=None):
+ """setnphi {[x y z]} -- sets or displays n_phi reference"""
+ if xyz is None:
+ ubcalc.print_reference()
+ else:
+ ubcalc.set_n_phi_configured(_to_column_vector_triple(xyz))
+ ubcalc.print_reference()
+
+@command
+def setnhkl(hkl=None):
+ """setnhkl {[h k l]} -- sets or displays n_hkl reference"""
+ if hkl is None:
+ ubcalc.print_reference()
+ else:
+ ubcalc.set_n_hkl_configured(_to_column_vector_triple(hkl))
+ ubcalc.print_reference()
+
+
+def _to_column_vector_triple(o):
+ m = matrix(o)
+ if m.shape == (1, 3):
+ return m.T
+ elif m.shape == (3, 1):
+ return m
+ else:
+ raise ValueError("Unexpected shape matrix: " + m)
+
+### UB refelections ###
+
+@command
+def showref():
+ """showref -- shows full reflection list"""
+ if ubcalc._state.reflist:
+ print '\n'.join(ubcalc._state.reflist.str_lines())
+ else:
+ print "<<< No reflections stored >>>"
+
+@command
+def addref(*args):
+ """
+ addref -- add reflection interactively
+ addref [h k l] {'tag'} -- add reflection with current position and energy
+ addref [h k l] (p1, .., pN) energy {'tag'} -- add arbitrary reflection
+ """
+
+ if len(args) == 0:
+ h = promptForNumber('h', 0.)
+ k = promptForNumber('k', 0.)
+ l = promptForNumber('l', 0.)
+ if None in (h, k, l):
+ _handleInputError("h,k and l must all be numbers")
+ reply = promptForInput('current pos', 'y')
+ if reply in ('y', 'Y', 'yes'):
+ positionList = settings.hardware.get_position() # @UndefinedVariable
+ energy = settings.hardware.get_energy() # @UndefinedVariable
+ else:
+ currentPos = settings.hardware.get_position() # @UndefinedVariable
+ positionList = []
+ names = settings.hardware.get_axes_names() # @UndefinedVariable
+ for i, angleName in enumerate(names):
+ val = promptForNumber(angleName.rjust(7), currentPos[i])
+ if val is None:
+ _handleInputError("Please enter a number, or press"
+ " Return to accept default!")
+ return
+ positionList.append(val)
+ muliplier = settings.hardware.energyScannableMultiplierToGetKeV # @UndefinedVariable
+ energy = promptForNumber('energy', settings.hardware.get_energy() / muliplier) # @UndefinedVariable
+ if val is None:
+ _handleInputError("Please enter a number, or press "
+ "Return to accept default!")
+ return
+ energy = energy * muliplier
+ tag = promptForInput("tag")
+ if tag == '':
+ tag = None
+ pos = settings.geometry.physical_angles_to_internal_position(positionList) # @UndefinedVariable
+ ubcalc.add_reflection(h, k, l, pos, energy, tag,
+ datetime.now())
+ elif len(args) in (1, 2, 3, 4):
+ args = list(args)
+ h, k, l = args.pop(0)
+ if not (isnum(h) and isnum(k) and isnum(l)):
+ raise TypeError()
+ if len(args) >= 2:
+ pos = settings.geometry.physical_angles_to_internal_position( # @UndefinedVariable
+ args.pop(0))
+ energy = args.pop(0)
+ if not isnum(energy):
+ raise TypeError()
+ else:
+ pos = settings.geometry.physical_angles_to_internal_position( # @UndefinedVariable
+ settings.hardware.get_position()) # @UndefinedVariable
+ energy = settings.hardware.get_energy() # @UndefinedVariable
+ if len(args) == 1:
+ tag = args.pop(0)
+ if not isinstance(tag, str):
+ raise TypeError()
+ else:
+ tag = None
+ ubcalc.add_reflection(h, k, l, pos, energy, tag,
+ datetime.now())
+ else:
+ raise TypeError()
+
+@command
+def editref(num):
+ """editref num -- interactively edit a reflection.
+ """
+ num = int(num)
+
+ # Get old reflection values
+ [oldh, oldk, oldl], oldExternalAngles, oldEnergy, oldTag, oldT = \
+ ubcalc.get_reflection_in_external_angles(num)
+ del oldT # current time will be used.
+
+ h = promptForNumber('h', oldh)
+ k = promptForNumber('k', oldk)
+ l = promptForNumber('l', oldl)
+ if None in (h, k, l):
+ _handleInputError("h,k and l must all be numbers")
+ reply = promptForInput('update position with current hardware setting',
+ 'n')
+ if reply in ('y', 'Y', 'yes'):
+ positionList = settings.hardware.get_position() # @UndefinedVariable
+ energy = settings.hardware.get_energy() # @UndefinedVariable
+ else:
+ positionList = []
+ names = settings.hardware.get_axes_names() # @UndefinedVariable
+ for i, angleName in enumerate(names):
+ val = promptForNumber(angleName.rjust(7), oldExternalAngles[i])
+ if val is None:
+ _handleInputError("Please enter a number, or press "
+ "Return to accept default!")
+ return
+ positionList.append(val)
+ muliplier = settings.hardware.energyScannableMultiplierToGetKeV # @UndefinedVariable
+ energy = promptForNumber('energy', oldEnergy / muliplier)
+ if val is None:
+ _handleInputError("Please enter a number, or press Return "
+ "to accept default!")
+ return
+ energy = energy * muliplier
+ tag = promptForInput("tag", oldTag)
+ if tag == '':
+ tag = None
+ pos = settings.geometry.physical_angles_to_internal_position(positionList) # @UndefinedVariable
+ ubcalc.edit_reflection(num, h, k, l, pos, energy, tag,
+ datetime.now())
+
+@command
+def delref(num):
+ """delref num -- deletes a reflection (numbered from 1)
+ """
+ ubcalc.del_reflection(int(num))
+
+@command
+def clearref():
+ """clearref -- deletes all the reflections
+ """
+ while ubcalc.get_number_reflections():
+ ubcalc.del_reflection(1)
+
+@command
+def swapref(num1=None, num2=None):
+ """
+ swapref -- swaps first two reflections used for calulating U matrix
+ swapref num1 num2 -- swaps two reflections (numbered from 1)
+ """
+ if num1 is None and num2 is None:
+ ubcalc.swap_reflections(1, 2)
+ elif isinstance(num1, int) and isinstance(num2, int):
+ ubcalc.swap_reflections(num1, num2)
+ else:
+ raise TypeError()
+
+### U calculation from crystal orientation
+@command
+def showorient():
+ """showorient -- shows full list of crystal orientations"""
+ if ubcalc._state.orientlist:
+ print '\n'.join(ubcalc._state.orientlist.str_lines())
+ else:
+ print "<<< No crystal orientations stored >>>"
+
+@command
+def addorient(*args):
+ """
+ addorient -- add crystal orientation interactively
+ addorient [h k l] [x y z] {'tag'} -- add crystal orientation in laboratory frame
+ """
+
+ if len(args) == 0:
+ h = promptForNumber('h', 0.)
+ k = promptForNumber('k', 0.)
+ l = promptForNumber('l', 0.)
+ if None in (h, k, l):
+ _handleInputError("h,k and l must all be numbers")
+
+ x = promptForNumber('x', 0.)
+ y = promptForNumber('y', 0.)
+ z = promptForNumber('z', 0.)
+ if None in (x, y, z):
+ _handleInputError("x,y and z must all be numbers")
+
+ tag = promptForInput("tag")
+ if tag == '':
+ tag = None
+ ubcalc.add_orientation(h, k, l, x , y, z, tag,
+ datetime.now())
+ elif len(args) in (1, 2, 3):
+ args = list(args)
+ h, k, l = args.pop(0)
+ if not (isnum(h) and isnum(k) and isnum(l)):
+ raise TypeError()
+ x, y, z = args.pop(0)
+ if not (isnum(x) and isnum(y) and isnum(z)):
+ raise TypeError()
+ if len(args) == 1:
+ tag = args.pop(0)
+ if not isinstance(tag, str):
+ raise TypeError()
+ else:
+ tag = None
+ ubcalc.add_orientation(h, k, l, x, y ,z, tag,
+ datetime.now())
+ else:
+ raise TypeError()
+
+@command
+def editorient(num):
+ """editorient num -- interactively edit a crystal orientation.
+ """
+ num = int(num)
+
+ # Get old reflection values
+ [oldh, oldk, oldl], [oldx, oldy, oldz], oldTag, oldT = \
+ ubcalc.get_orientation(num)
+ del oldT # current time will be used.
+
+ h = promptForNumber('h', oldh)
+ k = promptForNumber('k', oldk)
+ l = promptForNumber('l', oldl)
+ if None in (h, k, l):
+ _handleInputError("h,k and l must all be numbers")
+ x = promptForNumber('x', oldx)
+ y = promptForNumber('y', oldy)
+ z = promptForNumber('z', oldz)
+ if None in (x, y, z):
+ _handleInputError("x,y and z must all be numbers")
+ tag = promptForInput("tag", oldTag)
+ if tag == '':
+ tag = None
+ ubcalc.edit_orientation(num, h, k, l, x, y, z, tag,
+ datetime.now())
+
+@command
+def delorient(num):
+ """delorient num -- deletes a crystal orientation (numbered from 1)
+ """
+ ubcalc.del_orientation(int(num))
+
+@command
+def clearorient():
+ """clearorient -- deletes all the crystal orientations
+ """
+ while ubcalc.get_number_orientations():
+ ubcalc.del_orientation(1)
+
+@command
+def swaporient(num1=None, num2=None):
+ """
+ swaporient -- swaps first two crystal orientations used for calulating U matrix
+ swaporient num1 num2 -- swaps two crystal orientations (numbered from 1)
+ """
+ if num1 is None and num2 is None:
+ ubcalc.swap_orientations(1, 2)
+ elif isinstance(num1, int) and isinstance(num2, int):
+ ubcalc.swap_orientations(num1, num2)
+ else:
+ raise TypeError()
+
+
+### UB calculations ###
+
+@command
+def setu(U=None):
+ """setu {[[..][..][..]]} -- manually set U matrix
+ """
+ if U is None:
+ U = _promptFor3x3MatrixDefaultingToIdentity()
+ if U is None:
+ return # an error will have been printed or thrown
+ if _is3x3TupleOrList(U) or _is3x3Matrix(U):
+ ubcalc.set_U_manually(U)
+ else:
+ raise TypeError("U must be given as 3x3 list or tuple")
+
+@command
+def setub(UB=None):
+ """setub {[[..][..][..]]} -- manually set UB matrix"""
+ if UB is None:
+ UB = _promptFor3x3MatrixDefaultingToIdentity()
+ if UB is None:
+ return # an error will have been printed or thrown
+ if _is3x3TupleOrList(UB):
+ ubcalc.set_UB_manually(UB)
+ else:
+ raise TypeError("UB must be given as 3x3 list or tuple")
+
+def _promptFor3x3MatrixDefaultingToIdentity():
+ estring = "Please enter a number, or press Return to accept default!"
+ row1 = promptForList("row1", (1, 0, 0))
+ if row1 is None:
+ _handleInputError(estring)
+ return None
+ row2 = promptForList("row2", (0, 1, 0))
+ if row2 is None:
+ _handleInputError(estring)
+ return None
+ row3 = promptForList("row3", (0, 0, 1))
+ if row3 is None:
+ _handleInputError(estring)
+ return None
+ return [row1, row2, row3]
+
+@command
+def calcub():
+ """calcub -- (re)calculate U matrix from ref1 and ref2.
+ """
+ ubcalc.calculate_UB()
+
+@command
+def trialub():
+ """trialub -- (re)calculate U matrix from ref1 only (check carefully).
+ """
+ ubcalc.calculate_UB_from_primary_only()
+
+@command
+def orientub():
+ """orientub -- (re)calculate U matrix from orient1 and orient2.
+ """
+ ubcalc.calculate_UB_from_orientation()
+
+
+ # This command requires the ubcalc
+
+def checkub():
+ """checkub -- show calculated and entered hkl values for reflections.
+ """
+
+ s = "\n %7s %4s %4s %4s %6s %6s %6s TAG\n" % \
+ ('ENERGY', 'H', 'K', 'L', 'H_COMP', 'K_COMP', 'L_COMP')
+ s = bold(s)
+ nref = ubcalc.get_number_reflections()
+ if not nref:
+ s += "<>"
+ for n in range(nref):
+ hklguess, pos, energy, tag, _ = ubcalc.get_reflection(n + 1)
+ wavelength = 12.39842 / energy
+ hkl = settings.angles_to_hkl_function(pos.inRadians(), wavelength, ubcalc.UB)
+ h, k, l = hkl
+ if tag is None:
+ tag = ""
+ s += ("% 2d % 6.4f % 4.2f % 4.2f % 4.2f % 6.4f % 6.4f "
+ "% 6.4f %6s\n" % (n + 1, energy, hklguess[0],
+ hklguess[1], hklguess[2], h, k, l, tag))
+ print s
+
+
+@command
+def addmiscut(*args):
+ """addmiscut angle {[x y z]} -- apply miscut to U matrix using a specified miscut angle in degrees and a rotation axis"""
+
+ if len(args) == 0:
+ _handleInputError("Please specify a miscut angle in degrees "
+ "and, optionally, a rotation axis (default: [0 1 0])")
+ else:
+ args=list(args)
+ angle = args.pop(0)
+ rad_angle = float(angle) * TORAD
+ if len(args) == 0:
+ xyz = None
+ else:
+ xyz = args.pop(0)
+ ubcalc.set_miscut(xyz, rad_angle, True)
+
+@command
+def setmiscut(*args):
+ """setmiscut angle {[x y z]} -- manually set U matrix using a specified miscut angle in degrees and a rotation axis (default: [0 1 0])"""
+
+ if len(args) == 0:
+ _handleInputError("Please specify a miscut angle in degrees "
+ "and, optionally, a rotation axis (default: [0 1 0])")
+ else:
+ args=list(args)
+ angle = args.pop(0)
+ rad_angle = float(angle) * TORAD
+ if len(args) == 0:
+ xyz = None
+ else:
+ xyz = args.pop(0)
+ ubcalc.set_miscut(xyz, rad_angle, False)
+
+commands_for_help = ['State',
+ newub,
+ loadub,
+ lastub,
+ listub,
+ rmub,
+ saveubas,
+ ub,
+ 'Lattice',
+ setlat,
+ c2th,
+ hklangle]
+
+if ubcalc.include_reference:
+ commands_for_help.extend([
+ 'Reference (surface)',
+ setnphi,
+ setnhkl])
+
+if ubcalc.include_sigtau:
+ commands_for_help.extend([
+ 'Surface',
+ sigtau])
+
+commands_for_help.extend([
+ 'Reflections',
+ showref,
+ addref,
+ editref,
+ delref,
+ clearref,
+ swapref,
+ 'Orientations',
+ showorient,
+ addorient,
+ editorient,
+ delorient,
+ clearorient,
+ swaporient,
+ 'UB matrix',
+ checkub,
+ setu,
+ setub,
+ calcub,
+ orientub,
+ trialub,
+ refineub,
+ addmiscut,
+ setmiscut])
+
+
+
+def _is3x3TupleOrList(m):
+ if type(m) not in (list, tuple):
+ return False
+ if len(m) != 3:
+ return False
+ for mrow in m:
+ if type(mrow) not in (list, tuple):
+ return False
+ if len(mrow) != 3:
+ return False
+ return True
+
+
+def _is3x3Matrix(m):
+ return isinstance(m, matrix) and tuple(m.shape) == (3, 3)
+
+
+def _handleInputError(msg):
+ raise TypeError(msg)
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/diffcalc/util.py b/script/__Lib/diffcalc-2.1/diffcalc/util.py
new file mode 100755
index 0000000..873040c
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcalc/util.py
@@ -0,0 +1,347 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+from math import pi, acos, cos, sin
+from functools import wraps
+import textwrap
+
+try:
+ from gda.jython.commands.InputCommands import requestInput as raw_input
+ GDA = True
+except ImportError:
+ GDA = False
+ pass # raw_input unavailable in gda
+try:
+ from numpy import matrix
+ from numpy.linalg import norm
+except ImportError:
+ from numjy import matrix
+ from numjy.linalg import norm
+
+
+# from http://physics.nist.gov/
+h_in_eV_per_s = 4.135667516E-15
+c = 299792458
+TWELVEISH = c * h_in_eV_per_s # 12.39842
+
+
+SMALL = 1e-10
+TORAD = pi / 180
+TODEG = 180 / pi
+
+
+COLOURISE_TERMINAL_OUTPUT = not GDA
+
+def bold(s):
+ if not COLOURISE_TERMINAL_OUTPUT:
+ return s
+ else:
+ BOLD = '\033[1m'
+ END = '\033[0m'
+ return BOLD + s + END
+
+
+def x_rotation(th):
+ return matrix(((1, 0, 0), (0, cos(th), -sin(th)), (0, sin(th), cos(th))))
+
+
+def y_rotation(th):
+ return matrix(((cos(th), 0, sin(th)), (0, 1, 0), (-sin(th), 0, cos(th))))
+
+
+def z_rotation(th):
+ return matrix(((cos(th), -sin(th), 0), (sin(th), cos(th), 0), (0, 0, 1)))
+
+
+def xyz_rotation(u, angle):
+ u = [list(u), [0, 0, 0], [0, 0, 0]]
+ u = matrix(u) / norm(matrix(u))
+ e11=u[0,0]**2+(1-u[0,0]**2)*cos(angle)
+ e12=u[0,0]*u[0,1]*(1-cos(angle))-u[0,2]*sin(angle)
+ e13=u[0,0]*u[0,2]*(1-cos(angle))+u[0,1]*sin(angle)
+ e21=u[0,0]*u[0,1]*(1-cos(angle))+u[0,2]*sin(angle)
+ e22=u[0,1]**2+(1-u[0,1]**2)*cos(angle)
+ e23=u[0,1]*u[0,2]*(1-cos(angle))-u[0,0]*sin(angle)
+ e31=u[0,0]*u[0,2]*(1-cos(angle))-u[0,1]*sin(angle)
+ e32=u[0,1]*u[0,2]*(1-cos(angle))+u[0,0]*sin(angle)
+ e33=u[0,2]**2+(1-u[0,2]**2)*cos(angle)
+ return matrix([[e11,e12,e13],[e21,e22,e23],[e31,e32,e33]])
+
+
+class DiffcalcException(Exception):
+ """Error caused by user misuse of diffraction calculator.
+ """
+ def __str__(self):
+ lines = []
+ for msg_line in self.message.split('\n'):
+ lines.append('* ' + msg_line)
+ width = max(len(l) for l in lines)
+ lines.insert(0, '\n\n' + '*' * width)
+ lines.append('*' * width)
+ return '\n'.join(lines)
+
+
+class AbstractPosition(object):
+
+ def inRadians(self):
+ pos = self.clone()
+ pos.changeToRadians()
+ return pos
+
+ def inDegrees(self):
+ pos = self.clone()
+ pos.changeToDegrees()
+ return pos
+
+ def changeToRadians(self):
+ raise NotImplementedError()
+
+ def changeToDegrees(self):
+ raise NotImplementedError()
+
+ def totuple(self):
+ raise NotImplementedError()
+
+
+### Matrices
+
+def cross3(x, y):
+ """z = cross3(x ,y) -- where x, y & z are 3*1 Jama matrices"""
+ [[x1], [x2], [x3]] = x.tolist()
+ [[y1], [y2], [y3]] = y.tolist()
+ return matrix([[x2 * y3 - x3 * y2],
+ [x3 * y1 - x1 * y3],
+ [x1 * y2 - x2 * y1]])
+
+
+def dot3(x, y):
+ """z = dot3(x ,y) -- where x, y are 3*1 Jama matrices"""
+ return x[0, 0] * y[0, 0] + x[1, 0] * y[1, 0] + x[2, 0] * y[2, 0]
+
+
+def angle_between_vectors(a, b):
+ costheta = dot3(a * (1 / norm(a)), b * (1 / norm(b)))
+ return acos(bound(costheta))
+
+
+## Math
+
+def bound(x):
+ """
+ moves x between -1 and 1. Used to correct for rounding errors which may
+ have moved the sin or cosine of a value outside this range.
+ """
+ if abs(x) > (1 + SMALL):
+ raise AssertionError(
+ "The value (%f) was unexpectedly too far outside -1 or 1 to "
+ "safely bound. Please report this." % x)
+ if x > 1:
+ return 1
+ if x < -1:
+ return -1
+ return x
+
+
+def matrixToString(m):
+ ''' str = matrixToString(m) --- displays a Jama matrix m as a string
+ '''
+ toReturn = ''
+ for row in m.array:
+ for el in row:
+ toReturn += str(el) + '\t'
+ toReturn += '\n'
+ return toReturn
+
+
+def nearlyEqual(first, second, tolerance):
+ if type(first) in (int, float):
+ return abs(first - second) <= tolerance
+
+ if type(first) != type(matrix([[1]])):
+ # lists
+ first = matrix([list(first)])
+ second = matrix([list(second)])
+ diff = first - (second)
+ return norm(diff) <= tolerance
+
+
+def radiansEquivilant(first, second, tolerance):
+ if abs(first - second) <= tolerance:
+ return True
+ if abs((first - 2 * pi) - second) <= tolerance:
+ return True
+ if abs((first + 2 * pi) - second) <= tolerance:
+ return True
+ if abs(first - (second - 2 * pi)) <= tolerance:
+ return True
+ if abs(first - (second + 2 * pi)) <= tolerance:
+ return True
+
+ return False
+
+
+def degreesEquivilant(first, second, tolerance):
+ return radiansEquivilant(first * TORAD, second * TORAD, tolerance)
+
+
+def differ(first, second, tolerance):
+ """Returns error message if the norm of the difference between two arrays
+ or numbers is greater than the given tolerance. Else returns False.
+ """
+ # TODO: Fix spaghetti
+ nonArray = False
+ if type(first) in (int, float):
+ if type(second) not in (int, float):
+ raise TypeError(
+ "If first is an int or float, so must second. "
+ "first=%s, second=%s" & (repr(first), repr(second)))
+ first = [first]
+ second = [second]
+ nonArray = True
+ if not isinstance(first, matrix):
+ first = matrix([list(first)])
+ if not isinstance(second, matrix):
+ second = matrix([list(second)])
+ diff = first - second
+ if norm(diff) >= tolerance:
+ if nonArray:
+ return ('%s!=%s' %
+ (repr(first.tolist()[0][0]), repr(second.tolist()[0][0])))
+ return ('%s!=%s' %
+ (repr(tuple(first.tolist()[0])),
+ repr(tuple(second.tolist()[0]))))
+ return False
+
+
+### user input
+
+def getInputWithDefault(prompt, default=""):
+ """
+ Prompts user for input and returns if possible a float or a list of floats,
+ or if failing this a string. default may be a number, array of numbers,
+ or string.
+ """
+ if default is not "":
+ # Generate default string
+ if type(default) in (list, tuple):
+ defaultString = ""
+ for val in default:
+ defaultString += str(val) + ' '
+ defaultString = defaultString.strip()
+ else:
+ defaultString = str(default)
+ prompt = str(prompt) + '[' + defaultString + ']: '
+ else:
+ prompt = str(prompt) + ': '
+
+ rawresult = raw_input(prompt)
+
+ # Return default if no input provided
+ if rawresult == "":
+ return default
+
+ # Try to process result into list of numbers
+ try:
+ result = []
+ for val in rawresult.split():
+ result.append(float(val))
+ except ValueError:
+ # return a string
+ return rawresult
+ if len(result) == 1:
+ result = result[0]
+ return result
+
+
+class MockRawInput(object):
+ def __init__(self, toReturnList):
+ if type(toReturnList) != list:
+ toReturnList = [toReturnList]
+ self.toReturnList = toReturnList
+
+ def __call__(self, prompt):
+ toReturn = self.toReturnList.pop(0)
+ if type(toReturn) != str:
+ raise TypeError
+ print prompt + toReturn
+ return toReturn
+
+
+def getMessageFromException(e):
+ try: # Jython
+ return e.args[0]
+ except:
+ try: # Python
+ return e.message
+ except:
+ # Java
+ return e.args[0]
+
+
+def promptForNumber(prompt, default=""):
+ val = getInputWithDefault(prompt, default)
+ if type(val) not in (float, int):
+ return None
+ return val
+
+
+def promptForList(prompt, default=""):
+ val = getInputWithDefault(prompt, default)
+ if type(val) not in (list, tuple):
+ return None
+ return val
+
+
+def isnum(o):
+ return isinstance(o, (int, float))
+
+
+def allnum(l):
+ return not [o for o in l if not isnum(o)]
+
+
+DEBUG = False
+
+
+
+def command(f):
+ """A decorator to wrap a command method or function.
+
+ Calls to the decorated method or function are wrapped by call_command.
+ """
+ # TODO: remove one level of stack trace by not using wraps
+ @wraps(f)
+ def wrapper(*args, **kwds):
+ return call_command(f, args)
+
+ return wrapper
+
+
+def call_command(f, args):
+
+ if DEBUG:
+ return f(*args)
+ try:
+ return f(*args)
+ except TypeError, e:
+ # NOTE: TypeErrors resulting from bugs in the core code will be
+ # erroneously caught here! TODO: check depth of TypeError stack
+ raise TypeError(e.message + '\n\nUSAGE:\n' + f.__doc__)
+ except DiffcalcException, e:
+ # TODO: log and create a new one to shorten stack trace for user
+ raise DiffcalcException(e.message)
diff --git a/script/__Lib/diffcalc-2.1/diffcmd/__init__.py b/script/__Lib/diffcalc-2.1/diffcmd/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/script/__Lib/diffcalc-2.1/diffcmd/diffcalc_launcher.py b/script/__Lib/diffcalc-2.1/diffcmd/diffcalc_launcher.py
new file mode 100755
index 0000000..0653460
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcmd/diffcalc_launcher.py
@@ -0,0 +1,86 @@
+#!/usr/bin/python
+
+import argparse
+import subprocess
+import os
+import getpass
+
+DIFFCALC_BIN = os.path.split(os.path.realpath(__file__))[0]
+DIFFCALC_ROOT = os.path.abspath(os.path.join(DIFFCALC_BIN, os.pardir))
+
+MODULE_FOR_MANUALS = '_make_sixcircle_manual'
+
+def main():
+ parser = argparse.ArgumentParser(description='Diffcalc: A diffraction condition calculator of x-ray and neutron crystalography')
+ parser.add_argument('--modules', dest='show_modules', action='store_true',
+ help='list available modules')
+ parser.add_argument('--python', dest='use_python', action='store_true',
+ help='run within python rather than ipython')
+ parser.add_argument('--debug', dest='debug', action='store_true',
+ help='run in debug mode')
+ parser.add_argument('--make-manuals-source', dest='make_manuals', action='store_true',
+ help='make .rst manual files by running template through sixcircle')
+ parser.add_argument('--non-interactive', dest='non_interactive', action='store_true',
+ help='do not enter interactive mode after startup')
+ parser.add_argument('module', type=str, nargs='?',
+ help='the module to startup with')
+ args = parser.parse_args()
+
+ # Create list of available modules
+ module_names = []
+ for module_path in os.listdir(os.path.join(DIFFCALC_ROOT, 'startup')):
+ if not module_path.startswith('_') and module_path.endswith('.py'):
+ module_names.append(module_path.split('.')[0])
+ module_names.sort()
+
+ if args.show_modules:
+ print_available_modules(module_names)
+ exit(0)
+
+ if not args.make_manuals and not args.module:
+ print "A module name should be provided. Choose one of:"
+ print_available_modules(module_names)
+ exit(0)
+
+ if args.make_manuals:
+ if args.module:
+ print "When building the manuals no module should be given"
+ exit(1)
+ args.module = MODULE_FOR_MANUALS
+
+ if not args.make_manuals and args.module not in module_names:
+ print "The provided argument '%s' is not one of:" % args.module
+ print_available_modules(module_names)
+ exit(1)
+
+ env = os.environ.copy()
+
+ if 'PYTHONPATH' not in env:
+ env['PYTHONPATH'] = ''
+ env['PYTHONPATH'] = DIFFCALC_ROOT + ':' + env['PYTHONPATH']
+
+ diffcmd_start_path = os.path.join(DIFFCALC_ROOT, 'diffcmd', 'start.py')
+
+ if args.use_python:
+ cmd = 'python'
+ else: # ipython
+ cmd = 'ipython --no-banner --HistoryManager.hist_file=/tmp/ipython_hist_%s.sqlite' % getpass.getuser()
+
+ iflag = '' if args.non_interactive else '-i'
+ cmd = cmd + ' ' + ' '.join([iflag, diffcmd_start_path, args.module, str(args.debug)])
+
+ print 'Running: ' + cmd
+ rc = subprocess.call(cmd, env=env, shell=True)
+ exit(rc)
+
+
+def print_available_modules(module_names):
+ lines = []
+ for m in sorted(module_names):
+ lines.append(' ' + m)
+ print '\n'.join(lines)
+
+
+if __name__ == '__main__':
+ main()
+#
diff --git a/script/__Lib/diffcalc-2.1/diffcmd/diffcmd_utils.py b/script/__Lib/diffcalc-2.1/diffcmd/diffcmd_utils.py
new file mode 100755
index 0000000..57ed4aa
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcmd/diffcmd_utils.py
@@ -0,0 +1,43 @@
+#
+# General utility functions to handle Diffcalc commands
+#
+
+from gda.jython.commands.GeneralCommands import alias
+
+try:
+ import gda
+ GDA = True
+except ImportError:
+ GDA = False
+
+
+
+def alias_commands(global_namespace_dict):
+ """Alias commands left in global_namespace_dict by previous import from
+ diffcalc.
+
+ This is the equivalent of diffcmd/ipython/magic_commands() for use
+ when IPython is not available
+ """
+ gnd = global_namespace_dict
+ global GLOBAL_NAMESPACE_DICT
+ GLOBAL_NAMESPACE_DICT = gnd
+ print "Aliasing commands"
+
+ ### Alias commands in namespace ###
+ commands = gnd['hkl_commands_for_help']
+ commands += gnd['ub_commands_for_help']
+ if not GDA: # TODO: encapsulation issue: this should be done outside this function!
+ commands.append(gnd['pos'])
+ commands.append(gnd['scan'])
+ aliased_names = []
+
+ for f in commands:
+ # Skip section headers like 'Motion'
+ if not hasattr(f, '__call__'):
+ continue
+
+ alias(f.__name__)
+ aliased_names.append(f.__name__)
+
+ print "Aliased commands: " + ' '.join(aliased_names)
diff --git a/script/__Lib/diffcalc-2.1/diffcmd/ipython.py b/script/__Lib/diffcalc-2.1/diffcmd/ipython.py
new file mode 100755
index 0000000..81b4c33
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcmd/ipython.py
@@ -0,0 +1,302 @@
+import re
+from functools import wraps
+from IPython.core.magic import register_line_magic
+from IPython import get_ipython # @UnusedImport (used by register_line_magic)
+from diffcalc.gdasupport.scannable.hkl import Hkl
+
+"""
+For wrapping functions:
+
+ In [1]: import diffcmd.ipython
+
+ In [2]: diffcmd.ipython.GLOBAL_NAMESPACE_DICT = globals()
+
+ In [3]: from IPython.core.magic import register_line_magic
+
+ In [4]: from diffcmd.ipython import parse_line
+
+ In [5]: @register_line_magic
+ ...: @parse_line
+ ...: def check_parser(*args):
+ ...: return args
+ ...:
+
+ In [6]: check_parser
+ Out[6]:
+
+ In [7]: del check_parser
+
+ In [8]: check_parser
+ Out[8]: ()
+
+ In [9]: check_parser 1
+ Out[9]: (1,)
+
+ In [10]: check_parser 1 2
+ Out[10]: (1, 2)
+
+ In [11]: check_parser 1 2 [3]
+ Out[11]: (1, 2, [3])
+
+ In [12]: b='bbb'
+
+ In [13]: check_parser 1 2 [3] b
+ Out[13]: (1, 2, [3], 'bbb')
+
+
+And to create something dynamically from a function:
+
+ In [28]: def f(a, b, c):
+ ....: ....: return a, b, c
+ ....:
+
+ In [29]: register_line_magic(parse_line(f))
+ Out[29]:
+
+ In [30]: del f
+
+ In [31]: f 'a' -2 [1 3 -4]
+ Out[31]: ('a', -2, [1, 3, -4])
+
+And from a list of functions:
+
+ In [32]: def one(a):
+ ....: return a
+ ....:
+
+ In [33]: def two(a, b):
+ ....: return a, b
+ ....:
+
+ In [34]: functions = one, two
+
+ In [35]: del one, two
+
+ In [36]: for f in functions:
+ ....: register_line_magic(parse_line(f))
+ ....:
+
+ In [37]: one 1
+ Out[37]: 1
+
+ In [39]: two 1 2
+ Out[39]: (1, 2)
+
+And to check if we are running in iPython:
+
+ In [47]: 'get_ipython' in globals()
+ Out[47]: True
+
+def in_ipython():
+ try:
+ get_ipython()
+ return True
+ except NameError:
+ return False
+
+
+"""
+
+
+GLOBAL_NAMESPACE_DICT = {}
+
+MATH_OPERATORS = set(['-', '+', '/', '*'])
+
+# Keep a copy of python's original help as we may remove it later
+if 'help' in __builtins__:
+ ORIGINAL_PYTHON_HELP = __builtins__['help']
+
+
+COMMA_USAGE_HELP = \
+'''
+| When calling a function without brackets, whitespace must be used in
+| place of commas. For example:
+|
+| >>> function a b [1 2 3] 'c'
+|
+| is equivalent to:
+|
+| >>> function(a, b, [1, 2, 3], 'c')
+|
+'''
+
+
+MATH_OPERATOR_USAGE_HELP = \
+'''
+| When calling a function without brackets, whitespace is used in place of
+| commas. Therefore terms which require evaluation must contain no space.
+| These will fail for example:
+|
+| >>> function - 1
+| >>> function a() * 2
+
+| But this
+
+| >>> function -1 1-1 +1 a()+1 [-1 0+1 b()] c+1
+|
+| is okay and equivalent to:
+|
+| >>> function(-1, 0, 1, a() + 1, [-1, 1, b()], c + 1)
+|
+
+'''
+
+comma_finder = re.compile(r'''((?:[^,"']|"[^"]*"|'[^']*')+)''')
+space_finder = re.compile(r'''((?:[^ "']|"[^"]*"|'[^']*')+)''')
+hash_finder = re.compile(r'''((?:[^#"']|"[^"]*"|'[^']*')+)''')
+open_square_finder = re.compile(r'''((?:[^["']|"[^"]*"|'[^']*')+)''')
+close_square_finder = re.compile(r'''((?:[^]"']|"[^"]*"|'[^']*')+)''')
+
+def tokenify(s):
+
+ # Don't accept commas outside strings.
+ # Users are frustrated by not knowing when commas _are_ required.
+ # Making it clear when they are not helps them understand the
+ # difference.
+
+ if ',' in comma_finder.split(s):
+ print COMMA_USAGE_HELP
+ print "(string was: %s)" % s
+ raise SyntaxError('unexpected comma')
+
+ # ignore comment
+ hash_split = hash_finder.split(s)
+ if '#' in hash_split:
+ s = '' if hash_split[0] == '#' else hash_split[1]
+
+ # surround square brackets with spaces to simplify token extraction
+ s = ''.join(' [ ' if e == '[' else e for e in open_square_finder.split(s))
+ s = ''.join(' ] ' if e == ']' else e for e in close_square_finder.split(s))
+
+
+ # tokens are now separated by spaces
+
+ tokens = space_finder.split(s)[1::2]
+ tokens = [tok for tok in tokens if tok != '']
+ return tokens
+
+
+def parse(s, d):
+ s = str(s)
+ tokens = tokenify(s)
+ for tok in tokens:
+ if tok in MATH_OPERATORS:
+ print MATH_OPERATOR_USAGE_HELP
+ raise SyntaxError('could not evaluate: "%s"' % tok)
+
+
+ s = ', '.join(tokens)
+
+ s = s.replace('[, ', '[')
+ s = s.replace(',]', ']')
+ s = s.replace(', ]', ']')
+
+ try:
+ args = eval('[' + s + ']', d)
+ except SyntaxError:
+ raise SyntaxError('could not evaluate: "%s"' % s)
+ return args
+
+def parse_line(f, global_namespace_dict=None):
+ '''A decorator that parses a single string argument into a list of arguments
+ and calls the wrapped function with these.
+ '''
+ if not global_namespace_dict:
+ global_namespace_dict = GLOBAL_NAMESPACE_DICT
+ @wraps(f)
+ def wrapper(line):
+ args = parse(line, global_namespace_dict)
+ return f(*args)
+ return wrapper
+
+
+
+_DEFAULT_HELP = \
+"""
+For help with diffcalc's orientation phase try:
+
+ >>> help ub
+
+For help with moving in reciprocal lattice space try:
+
+ >>> help hkl
+
+For more detailed help try for example:
+
+ >>> help newub
+
+For help with driving axes or scanning:
+
+ >>> help pos
+ >>> help scan
+
+For help with regular python try for example:
+
+ >>> help list
+
+For more detailed help with diffcalc go to:
+
+ https://diffcalc.readthedocs.io
+
+"""
+
+def magic_commands(global_namespace_dict):
+ """Magic commands left in global_namespace_dict by previous import from
+ diffcalc.
+
+ Also creates a help command. NOTE that calling this will
+ remove the original commands from the global namespace as otherwise these
+ would shadow the ipython magiced versions.
+
+ Depends on hkl_commands_for_help & ub_commands_for_help list having been
+ left in the global namespace and assumes there is pos and scan command.
+ """
+ gnd = global_namespace_dict
+ global GLOBAL_NAMESPACE_DICT
+ GLOBAL_NAMESPACE_DICT = gnd
+
+ ### Magic commands in namespace ###
+ commands = list(gnd['hkl_commands_for_help'])
+ commands += gnd['ub_commands_for_help']
+ commands.append(gnd['pos'])
+ commands.append(gnd['scan'])
+ command_map = {}
+ for f in commands:
+ # Skip section headers like 'Motion'
+ if not hasattr(f, '__call__'):
+ continue
+ # magic the function and remove from namespace (otherwise it would
+ # shadow the magiced command)
+ register_line_magic(parse_line(f, gnd))
+ del gnd[f.__name__]
+ command_map[f.__name__] = f
+
+ ### Create help function ###
+ #Expects python's original help to be named pythons_help and to be
+ #available in the top-level global namespace (where non-diffcalc
+ #objects may have help called from).
+ def help(s): # @ReservedAssignment
+ """Diffcalc help for iPython
+ """
+ if s == '':
+ print _DEFAULT_HELP
+ elif s == 'hkl':
+ # Use help injected into hkl object
+ print Hkl.dynamic_docstring
+ elif s == 'ub':
+ # Use help injected into ub command
+ print command_map['ub'].__doc__
+ elif s in command_map:
+ print "%s (diffcalc command):" %s
+ print command_map[s].__doc__
+ else:
+ exec('pythons_help(%s)' %s, gnd)
+
+
+ ### Setup help command ###
+ gnd['pythons_help'] = ORIGINAL_PYTHON_HELP
+ register_line_magic(help)
+ # Remove builtin help
+ # (otherwise it would shadow magiced command
+ if 'help' in __builtins__:
+ del __builtins__['help']
diff --git a/script/__Lib/diffcalc-2.1/diffcmd/ipythonmagic.py b/script/__Lib/diffcalc-2.1/diffcmd/ipythonmagic.py
new file mode 100755
index 0000000..6679864
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcmd/ipythonmagic.py
@@ -0,0 +1,79 @@
+
+import diffcmd.ipython
+from IPython.core.magic import register_line_magic
+from diffcmd.ipython import parse_line
+
+command_map = {}
+
+_DEFAULT_HELP = """
+For help with diffcalc's orientation phase try:
+
+ >>> help ub
+
+For help with moving in reciprocal lattice space try:
+
+ >>> help hkl
+
+For more detailed help try for example:
+
+ >>> help newub
+
+For help with driving axes or scanning:
+
+ >>> help pos
+ >>> help scan
+
+For help with regular python try for example:
+
+ >>> help list
+
+For more detailed help with diffcalc go to:
+
+ https://diffcalc.readthedocs.io
+
+"""
+
+# This function should be called with parameter globals()
+def define_commands(dictionary):
+ print "Ipython detected - magicing commands"
+ magiced_names = []
+ commands = hkl_commands_for_help + ub_commands_for_help # @UndefinedVariable
+ commands += [pos, scan] # @UndefinedVariable
+ ipython.GLOBAL_NAMESPACE_DICT = dictionary
+ for f in commands:
+ # Skip section headers like 'Motion'
+ if not hasattr(f, '__call__'):
+ continue
+
+ # magic the function and remove from namespace (otherwise it would
+ # shadow the magiced command)
+ register_line_magic(parse_line(f))
+ del dictionary[f.__name__]
+ command_map[f.__name__] = f
+ magiced_names.append(f.__name__)
+
+ print "Magiced commands: " + ' '.join(magiced_names)
+
+ # because the functions have gone from namespace we need to override
+ pythons_help = __builtins__.help
+ del __builtins__.help
+
+ register_line_magic(help)
+ del help
+
+def help(s):
+ """Diffcalc help for iPython
+ """
+ if s == '':
+ print _DEFAULT_HELP
+ elif s == 'hkl':
+ # Use help injected into hkl object
+ print hkl.__doc__
+ elif s == 'ub':
+ # Use help injected into ub command
+ print command_map['ub'].__doc__
+ elif s in command_map:
+ print "%s (diffcalc command):" %s
+ print command_map[s].__doc__
+ else:
+ exec('pythons_help(%s)' %s)
diff --git a/script/__Lib/diffcalc-2.1/diffcmd/make_manual.py b/script/__Lib/diffcalc-2.1/diffcmd/make_manual.py
new file mode 100755
index 0000000..a3b77bf
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcmd/make_manual.py
@@ -0,0 +1,146 @@
+
+
+from StringIO import StringIO
+from IPython import get_ipython
+import sys
+from diffcalc.dc.help import format_commands_for_rst_table
+
+
+TEST_INPUT="""
+Diffcalc's Scannables
+=====================
+
+Please see :ref:`moving-in-hkl-space` and :ref:`scanning-in-hkl-space` for some relevant examples.
+
+To list and show the current positions of your beamline's scannables
+use ``pos`` with no arguments::
+
+ >>> pos wl
+
+should do nought, but this should be replaced::
+
+ ==> pos wl 2
+
+should do the thing
+
+ ==> abcd
+"""
+
+
+
+def echorun(magic_cmd):
+ print "\n>>> " + str(magic_cmd)
+
+
+
+def make_manual(input_file_path,
+ output_file_path,
+ ub_commands_for_help,
+ hkl_commands_for_help):
+
+ # Read input file (should be .rst file)
+ with open(input_file_path, 'r') as f:
+ input_string = f.read()
+
+ # Parse input string
+ output_lines = []
+ for lineno, line in enumerate(input_string.split('\n')):
+ process = '==>' in line
+
+ if process and 'STOP' in line:
+ print "'==> STOP' found on line. STOPPING", lineno + 1
+ return
+
+ elif process and 'UB_HELP_TABLE' in line:
+ print 'Creating UB help table'
+ output_lines_from_line = format_commands_for_rst_table(
+ '', ub_commands_for_help)
+
+ elif process and 'HKL_HELP_TABLE' in line:
+ print 'Creating HKL help table'
+ output_lines_from_line = format_commands_for_rst_table(
+ '', hkl_commands_for_help)
+
+ else:
+ output_lines_from_line = parse_line(
+ line, lineno + 1, input_file_path)
+
+# print '\n'.join(output_lines_from_line)
+ output_lines.extend(output_lines_from_line)
+
+ # Write output file
+ if output_file_path:
+ with open(output_file_path, 'w') as f:
+ f.write('\n'.join(output_lines))
+ print "Wrote file:", output_file_path
+# try:
+# if output_file_path:
+# orig_stdout = sys.stdout
+# f = file(output_file_path, 'w')
+# sys.stdout = f
+#
+#
+#
+# finally:
+# if output_file_path:
+# sys.stdout = orig_stdout
+# f.close()
+
+
+def parse_line(linein, lineno, filepath):
+ output_lines = []
+ if '==>' in linein:
+ pre, cmd = linein.split('==>')
+ _check_spaces_only(pre, lineno, filepath)
+ cmd = cmd.strip() # strip whitespace
+ output_lines.append(pre + ">>> " + cmd)
+ result_lines = _capture_magic_command_output(cmd, lineno, filepath)
+
+
+ # append to output
+ for line in result_lines:
+ output_lines.append(pre + line)
+ else:
+ output_lines.append(linein)
+ return output_lines
+
+
+def _check_spaces_only(s, lineno, filepath):
+ for c in s:
+ if c != ' ':
+ raise Exception('Error on line %i of %s :\n text proceeding --> must be '
+ 'spaces only' % (lineno, filepath))
+
+def _capture_magic_command_output(magic_cmd, lineno, filepath):
+ orig_stdout = sys.stdout
+ result = StringIO()
+ sys.stdout = result
+
+ def log_error():
+ msg = "Error on line %i of %s evaluating '%s'" % (lineno, filepath, magic_cmd)
+ sys.stderr.write('\n' + '=' * 79 + '\n' + msg + '\n' +'v' * 79 + '\n')
+ return msg
+
+ try:
+ line_magics = get_ipython().magics_manager.magics['line']
+ magic = magic_cmd.split(' ')[0]
+ if magic not in line_magics:
+ msg = log_error()
+ raise Exception(msg + " ('%s' is not a magic command)" % magic)
+ get_ipython().magic(magic_cmd)
+ except:
+ log_error()
+ raise
+ finally:
+ sys.stdout = orig_stdout
+
+ result_lines = result.getvalue().split('\n')
+
+ # trim trailing lines which are whitespace only
+ while result_lines and (result_lines[-1].isspace() or not result_lines[-1]):
+ result_lines.pop()
+
+ return result_lines
+
+
+
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/diffcmd/start.py b/script/__Lib/diffcalc-2.1/diffcmd/start.py
new file mode 100755
index 0000000..faa7b6d
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/diffcmd/start.py
@@ -0,0 +1,87 @@
+"""
+start the diffcmd environemt using a script from startup.
+This should normally be run by the main diffcalc.py program.
+
+with diffcalc on PYTHONPATH
+$ ipython -i -m diffcm.diffcmd module_name_string debug_bool
+"""
+from __future__ import absolute_import
+
+import diffcalc
+import diffcalc.settings
+import os
+import sys
+from diffcalc.ub.persistence import UBCalculationJSONPersister
+from diffcalc.util import bold
+import diffcalc.util
+import diffcalc.gdasupport.minigda.command
+DIFFCALC_ROOT = os.path.realpath(diffcalc.__file__).split('diffcalc/__init__.py')[0]
+
+try:
+ __IPYTHON__ # @UndefinedVariable
+ IPYTHON = True
+except NameError:
+ IPYTHON = False
+
+
+module_name = sys.argv[1] #3 if IPYTHON else 1]
+debug = sys.argv[2] == 'True' #4 if IPYTHON else 2])
+
+print
+print bold('-' * 34 + ' DIFFCALC ' + '-' * 35)
+
+# configure persisentence
+DIFFCALC_VAR = os.path.join(os.path.expanduser('~'), '.diffcalc', module_name)
+if not os.path.exists(DIFFCALC_VAR):
+ print "Making diffcalc var folder:'%s'" % DIFFCALC_VAR
+ os.makedirs(DIFFCALC_VAR)
+diffcalc.settings.ubcalc_persister = UBCalculationJSONPersister(DIFFCALC_VAR)
+
+# configure debug
+diffcalc.util.DEBUG = debug
+if debug:
+ print "WARNING: debug mode on; help for command syntax errors disabled."
+
+# import script
+script = os.path.join(DIFFCALC_ROOT, 'startup', module_name) + '.py'
+
+print "Startup script: '%s'" % script
+namespace = {}
+execfile(script, namespace)
+globals().update(namespace)
+diffcalc.gdasupport.minigda.command.ROOT_NAMESPACE_DICT = dict(namespace)
+print bold('-' * 36 + ' Help ' + '-' * 37)
+print HELP_STRING # @UndefinedVariable
+if 'LOCAL_MANUAL' in locals():
+ print "Local: " + LOCAL_MANUAL # @UndefinedVariable
+print bold('-' * 79)
+print
+
+
+# magic commands if IPython
+if IPYTHON:
+ from diffcmd.ipython import magic_commands
+ magic_commands(globals())
+
+
+if 'MANUALS_TO_MAKE' in locals():
+ summary_lines = ['Made manuals:']
+ from diffcmd.make_manual import make_manual
+ for source_path in MANUALS_TO_MAKE: # @UndefinedVariable
+ import diffcalc.ub.ub
+ try:
+ diffcalc.ub.ub.rmub('example')
+ except KeyError:
+ pass
+ target_path = source_path.replace('_template', '')
+ print '@' * 79
+ print "Making manual"
+ print " Source:", source_path
+ print " Target:", target_path
+
+ make_manual(source_path, target_path,
+ ub_commands_for_help, # @UndefinedVariable
+ hkl_commands_for_help) # @UndefinedVariable
+ summary_lines.append(' - ' + source_path + ' -- > ' + target_path)
+ print '\n'.join(summary_lines)
+
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/doc/Makefile b/script/__Lib/diffcalc-2.1/doc/Makefile
new file mode 100755
index 0000000..032954a
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/Makefile
@@ -0,0 +1,193 @@
+# Makefile for documentation
+# This makefile is a modified version of the makefile generated by the sphinx-quickstart command
+
+# set environment for Diamond Light Source
+ifeq ($(CONTEXT),diamond)
+ @echo "Environment variable CONTEXT=diamond, so setting build environment suitable for Diamond Light Source"
+ # get the location of a Python that has Sphinx installed
+ SPHINXBUILD?=$(shell module load python/2.7.2;which sphinx-build)
+ # set http proxy
+ export http_proxy?=http://wwwcache.rl.ac.uk:8080
+ export https_proxy?=https://wwwcache.rl.ac.uk:8080
+endif
+
+# optionally use Sphinx's "-W" options, which converts warnings into errors
+ifeq ($(HALTONWARNING),y)
+ SPHINXEXTRAOPT=-W
+else ifeq ($(HALTONWARNING),Y)
+ SPHINXEXTRAOPT=-W
+else
+ SPHINXEXTRAOPT=
+endif
+
+### <-- start of Makefile contents generated by sphinx-quickstart --> ###
+
+# You can set these variables from the command line.
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+PAPER ?=
+BUILDDIR ?= build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXEXTRAOPT) $(SPHINXOPTS) source
+
+.PHONY: help helpfull pwd clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest pdfa4 pdfletter pdf all
+
+help:
+ @echo
+ @echo "Please use \`make ' where is one of"
+ @echo " html to make standalone HTML files"
+ @echo " pdf to create pdf files for both A4- and Letter-sized paper"
+ @echo " pdfa4 to create pdf files for A4-sized paper"
+ @echo " pdfletter to create pdf files for Letter-sized paper"
+ @echo " all to build html and pdf"
+ @echo " clean to wipe the build directory"
+ @echo
+ @echo "You can use \`make helpfull' to get a full list of targets (not all have been tested)"
+
+helpfull:
+ @echo "Please use \`make ' where is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+pwd:
+ @echo
+ @echo "*******************************************************************************************************"
+ @echo "Current directory = "`pwd`
+ @echo "*******************************************************************************************************"
+
+clean: pwd
+ -rm -rf $(BUILDDIR)/*
+
+html: pwd
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/GDA.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/GDA.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/GDA"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/GDA"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex: pwd
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf: pwd
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ make -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+### <-- end of Makefile contents generated by sphinx-quickstart --> ###
+
+pdfa4: pwd
+ rm -rf $(BUILDDIR)/latex
+ rm -rf $(BUILDDIR)/pdf-a4
+ make latexpdf PAPER=a4
+ mkdir $(BUILDDIR)/pdf-a4/
+ cp $(BUILDDIR)/latex/*.pdf $(BUILDDIR)/pdf-a4/.
+ @echo
+ @echo "Build finished for A4-sized PDFs. The PDF files are in $(BUILDDIR)/pdf-a4."
+
+pdfletter: pwd
+ rm -rf $(BUILDDIR)/latex
+ rm -rf $(BUILDDIR)/pdf-letter
+ make latexpdf PAPER=letter
+ mkdir $(BUILDDIR)/pdf-letter/
+ cp $(BUILDDIR)/latex/*.pdf $(BUILDDIR)/pdf-letter/.
+ @echo
+ @echo "Build finished for Letter-sized PDFs. The PDF files are in $(BUILDDIR)/pdf-letter."
+
+pdf: pwd pdfa4 pdfletter
+
+all: pwd html pdf
diff --git a/script/__Lib/diffcalc-2.1/doc/docs-build-diffcalc_doc-linux.launch b/script/__Lib/diffcalc-2.1/doc/docs-build-diffcalc_doc-linux.launch
new file mode 100755
index 0000000..0bba525
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/docs-build-diffcalc_doc-linux.launch
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/script/__Lib/diffcalc-2.1/doc/references b/script/__Lib/diffcalc-2.1/doc/references
new file mode 100755
index 0000000..a094e55
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/references
@@ -0,0 +1,22 @@
+
+.. [You1999] H. You. *Angle calculations for a '4S+2D' six-circle diffractometer.*
+ J. Appl. Cryst. (1999). **32**, 614-623. `(pdf link)
+ `__.
+
+.. [Busing1967] W. R. Busing and H. A. Levy. *Angle calculations for 3- and 4-circle X-ray
+ and neutron diffractometers.* Acta Cryst. (1967). **22**, 457-464. `(pdf link)
+ `__.
+
+.. [Vlieg1993] Martin Lohmeier and Elias Vlieg. *Angle calculations for a six-circle
+ surface x-ray diffractometer.* J. Appl. Cryst. (1993). **26**, 706-716. `(pdf link)
+ `__.
+
+.. [Vlieg1998] Elias Vlieg. *A (2+3)-type surface diffractometer: mergence of the z-axis and
+ (2+2)-type geometries.* J. Appl. Cryst. (1998). **31**, 198-203. `(pdf link)
+ `__.
+
+.. [Willmott2011] C. M. Schlepütz, S. O. Mariager, S. A. Pauli, R. Feidenhans'l and
+ P. R. Willmott. *Angle calculations for a (2+3)-type diffractometer: focus
+ on area detectors.* J. Appl. Cryst. (2011). **44**, 73-83. `(pdf link)
+ `__.
+
diff --git a/script/__Lib/diffcalc-2.1/doc/source/ACKS.rst b/script/__Lib/diffcalc-2.1/doc/source/ACKS.rst
new file mode 100755
index 0000000..81fb355
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/source/ACKS.rst
@@ -0,0 +1,34 @@
+################
+Acknowledgements
+################
+
+.. toctree::
+ :maxdepth: 2
+ :numbered:
+
+We would like to acknowledge the people who have made a direct impact on the
+Diffcalc project, knowingly or not, in terms of encouragement, suggestions,
+criticism, bug reports, code contributions, and related projects.
+
+Names are ordered alphabetically by surname.
+
+.. If you add new entries, keep the list sorted by surname!
+
+.. acks::
+
+ * Allesandro Bombardi
+ * Mark Booth
+ * W. R. Busing
+ * Steve Collins
+ * Mirian Garcia-Fernandez
+ * H. A. Levy
+ * Martin Lohmier
+ * Chris Nicklin
+ * Elias Vlieg --- writer of DIF software used as a model for Diffcalc
+ * Robert Walton
+ * H. You
+ * Fajin Yuan
+
+Thank you!
+
+Rob Walton & Irakli Sikharulidze
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/doc/source/conf.py b/script/__Lib/diffcalc-2.1/doc/source/conf.py
new file mode 100755
index 0000000..fe9ba1d
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/source/conf.py
@@ -0,0 +1,243 @@
+# -*- coding: utf-8 -*-
+
+#
+# documentation build configuration file, created by
+# sphinx-quickstart on Fri Apr 15 10:03:07 2011.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os, time
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.extlinks', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.imgmath', 'sphinx.ext.ifconfig']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Diffcalc'
+copyright = u'2017-%s, Diamond Light Source' % time.strftime('%Y')
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '2.1'
+# The full version, including alpha/beta/rc tags.
+release = '2.1'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+# html_theme_options = {
+# 'sidebarbgcolor' : '#f2f2f2',
+# 'sidebartextcolor': '#444a95',
+# 'sidebarlinkcolor': '#0b0f40',
+# }
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+
+import sphinx_rtd_theme
+
+html_theme = "sphinx_rtd_theme"
+
+html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# " v documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+html_logo = 'diffcalc_web.png'
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+#html_static_path = ['_static']
+html_static_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+html_show_sourcelink = False
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+html_show_sphinx = False
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+latex_paper_size = 'a4'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('youmanual', 'diffcalc_user_guide.tex', u'Diffcalc User Guide',
+ u'Diamond Light Source', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page
+latex_logo = 'diffcalc_pdf.png'
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+#man_pages = []
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {}
+
+"""
+ Additional options for Diffcalc
+"""
+
+todo_include_todos = True
+
+extlinks = {
+ 'opengda_url' :('http://www.opengda.org/%s', None),
+ }
+
+rst_prolog = """
+.. |DLS| replace:: :abbr:`DLS (Diamond Light Source)`
+"""
diff --git a/script/__Lib/diffcalc-2.1/doc/source/developer/contents.rst b/script/__Lib/diffcalc-2.1/doc/source/developer/contents.rst
new file mode 100755
index 0000000..d52fdd9
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/source/developer/contents.rst
@@ -0,0 +1,25 @@
+########################
+Diffcalc Developer Guide
+########################
+
+:Author: Rob Walton
+:Contact: rob.walton (at) diamond (dot) ac (dot) uk
+:Website: http://www.opengda.org/
+
+.. rubric:: Diffcalc: A diffraction condition calculator for diffractometer control
+
+.. toctree::
+ :maxdepth: 2
+ :numbered:
+
+ intro
+ package
+ quickstart_api
+ development
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/doc/source/developer/development.rst b/script/__Lib/diffcalc-2.1/doc/source/developer/development.rst
new file mode 100755
index 0000000..12b90c6
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/source/developer/development.rst
@@ -0,0 +1,24 @@
+Development
+===========
+
+The files are kept here_ on github_. See bootcamp for an introduction to
+using github. To contribute please fork the project. Otherwise you can make
+a read-only clone or export.
+
+Code format should follow pep8 guidelines. PyDev has a good pep8 checker.
+
+To run the tests install nose_, change directory into the test folder and run::
+
+ $ nosetests
+ .......... ...
+ ----------------------------------------------------------------------
+ Ran 3914 tests in 9.584s
+
+ OK (SKIP=15)
+
+
+
+.. _here: https://github.com/DiamondLightSource/diffcalc
+.. _github: https://github.com
+.. _nose: http://nose.readthedocs.org/en/latest/
+.. _pep8: http://www.python.org/dev/peps/pep-0008/
diff --git a/script/__Lib/diffcalc-2.1/doc/source/developer/intro.rst b/script/__Lib/diffcalc-2.1/doc/source/developer/intro.rst
new file mode 100755
index 0000000..a45eae4
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/source/developer/intro.rst
@@ -0,0 +1,55 @@
+Introduction
+============
+
+Diffcalc is a diffraction condition calculator used for controlling
+diffractometers within reciprocal lattice space. It performs the same
+task as the ``fourc``, ``sixc``, ``twoc``, ``kappa``, ``psic`` and
+``surf`` macros from SPEC_.
+
+Diffcalc's standard calculation engine is an implementation of
+[You1999]_ . The first versions of Diffcalc were based on
+[Vlieg1993]_ and [Vlieg1998]_ and a 'Vlieg' engine is still
+available. The 'You' engine is more generic and the plan is to remove
+the old 'Vlieg' engine once beamlines have been migrated. New users
+should use the 'You' engine.
+
+The foundations for this type of calculation were laid by by Busing &
+Levi in their classic paper [Busing1967]_. Diffcalc's orientation
+algorithm is taken from this paper. Busing & Levi also provided the
+original definition of the coordinate frames and of the U and B
+matrices used to describe a crystal's orientation and to convert between
+Cartesian and reciprical lattice space.
+
+Geometry plugins are used to adapt the six circle model used
+internally by Diffcalc to apply to other diffractometers. These
+contain a dictionary of the 'missing' angles which Diffcalc uses to
+constrain these angles internally, and a methods to map from external
+angles to Diffcalc angles and visa versa.
+
+Options to use Diffcalc:
+
+- **The User manual next to this developer manual or README file on github.**
+- The :ref:`quickstart-api` section describes how to run up only
+ the core in Python_. This provides a base option for system integration.
+
+Diffcalc will work with Python 2.7 or higher with numpy_, or with
+Jython 2.7 of higher with Jama_.
+
+
+.. [*] The very small 'Willmott' engine currently handles the case for
+ surface diffraction where the surface normal is held vertical
+ [Willmott2011]_. The 'You' engine handles this case fine, but
+ currently spins nu into an unhelpful quadrant. We hope to
+ remove the need for this engine soon.
+
+
+.. _SPEC: http://www.certif.com/
+.. _Python: http://python.org
+.. _IPython: http://http://ipython.org/
+.. _Jython: http://jython.org
+.. _OpenGDA: http://opengda.org
+.. _numpy: http://numpy.scipy.org/
+.. _Jama: http://math.nist.gov/javanumerics/jama/
+
+
+.. include:: ../../references
diff --git a/script/__Lib/diffcalc-2.1/doc/source/developer/package.rst b/script/__Lib/diffcalc-2.1/doc/source/developer/package.rst
new file mode 100755
index 0000000..e4d3210
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/source/developer/package.rst
@@ -0,0 +1,30 @@
+Project Files & Directories
+===========================
+
+diffcalc
+ The main source package.
+
+test
+ Diffcalcs unit-test package (use Nose_ to run them).
+
+diffcmd
+ A spec-like openGDA emulator.
+
+numjy
+ A *very* minimal implentation of numpy for jython. It supports only what
+ Diffcalc needs.
+
+doc
+ The documentation is written in reStructuredText and can be compiled into
+ html and pdf using Python's `Sphinx `_. With Sphinx
+ installed use ``make clean all`` from within the user and developer guide
+ folders to build the documentation.
+
+startup
+ Starup scripts called by diffcmd or openGDA to startup diffcalc
+
+model
+ Vrml models of diffractometers and a hokey script for animating then and
+ controlling them from diffcalc.
+
+.. _Nose: http://readthedocs.org/docs/nose/en/latest/
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/doc/source/developer/quickstart_api.rst b/script/__Lib/diffcalc-2.1/doc/source/developer/quickstart_api.rst
new file mode 100755
index 0000000..96bb9a2
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/source/developer/quickstart_api.rst
@@ -0,0 +1,266 @@
+.. _quickstart-api:
+
+.. warning:: This documentation is out of date. The README and the user doc has been updated recently. For now if you need help with API, please contact me at Diamond. -- Rob Walton
+
+Quick-Start: Python API
+=======================
+
+This section describes how to run up only the core in Python or
+IPython. This provides an API which could be used to integrate Diffcalc into an
+existing data acquisition system; although the interface described in
+the README would normally provide a better starting point.
+
+For a full description of what Diffcalc does and how to use it please
+see the 'Diffcalc user manual'.
+
+Setup environment
+-----------------
+
+.. include:: quickstart_setup_environment
+
+
+Start
+-----
+
+With Python start the sixcircle_api.py example startup script (notice
+the -i and -m) and call `demo_all()`::
+
+ $ python -i -m startup.api.sixcircle
+ >>> demo_all()
+
+IPython requires::
+
+ $ ipython -i startup/api/sixcircle.py
+ >>> demo_all()
+
+Alternatively start Python or IPython and cut and paste lines from the rest of
+this tutorial.
+
+Configure a diffraction calculator
+----------------------------------
+
+By default some exceptions are handled in a way to make user interaction
+friendlier. Switch this off with::
+
+ >>> import diffcalc.util
+ >>> diffcalc.util.DEBUG = True
+
+To setup a Diffcalc calculator, first configure `diffcalc.settings` module::
+
+ >>> from diffcalc import settings
+ >>> from diffcalc.hkl.you.geometry import SixCircle
+ >>> from diffcalc.hardware import DummyHardwareAdapter
+ >>> settings.hardware = DummyHardwareAdapter(('mu', 'delta', 'gam', 'eta', 'chi', 'phi'))
+ >>> settings.geometry = SixCircle() # @UndefinedVariable
+
+The hardware adapter is used by Diffcalc to read up the current angle
+settings, wavelength and axes limits. It is primarily used to simplify
+commands for end users. It could be dropped for this API use, but it
+is also used for the important job of checking axes limits while
+choosing solutions.
+
+Geometry plugins are used to adapt the six circle model used
+internally by Diffcalc to apply to other diffractometers. These
+contain a dictionary of the 'missing' angles which Diffcalc internally
+uses to constrain these angles, and a methods to map from
+external angles to Diffcalc angles and visa versa.
+
+
+Calling the API
+---------------
+
+The ``diffcalc.dc.dcyou`` module (and others) read the ``diffcalc.settings`` module when first
+imported. Note that this means that changes to the settings will most likely
+have no effect unless ``diffcalc.dc.dcyou`` is reloaded::
+
+ >>> import diffcalc.dc.dcyou as dc
+
+This includes the two critical functions::
+
+ def hkl_to_angles(h, k, l, energy=None):
+ """Convert a given hkl vector to a set of diffractometer angles
+
+ return angle tuple and virtual angles dictionary
+ """
+
+ def angles_to_hkl(angle_tuple, energy=None):
+ """Converts a set of diffractometer angles to an hkl position
+
+ Return hkl tuple and virtual angles dictionary
+ """
+
+``diffcalc.dc.dcyou`` also brings in all the commands from ``diffcalc.ub.ub``,
+``diffcalc.hardware`` and ``diffcalc.hkl.you.hkl``. That is it includes all the
+commands exposed in the top level namespace when diffcalc is used interactively::
+
+ >>> dir(dc)
+
+ ['__builtins__', '__doc__', '__file__', '__name__', '__package__',
+ '_hardware','_hkl', '_ub', 'addref', 'allhkl', 'angles_to_hkl', 'c2th',
+ 'calcub', 'checkub', 'clearref', 'con', 'constraint_manager', 'delref',
+ 'diffcalc', 'editref', 'energy_to_wavelength', 'hardware', 'hkl_to_angles',
+ 'hklcalc', 'lastub', 'listub', 'loadub', 'newub', 'rmub', 'saveubas', 'setcut',
+ 'setlat', 'setmax', 'setmin', 'settings', 'setu', 'setub', 'showref',
+ 'swapref', 'trialub', 'ub', 'ub_commands_for_help', 'ubcalc', 'uncon']
+
+This doesn't form the best API to program against though, so it is best to
+use the four modules more directly. The example below assumes you have
+also imported::
+
+ >>> from diffcalc.ub import ub
+ >>> from diffcalc import hardware
+ >>> from diffcalc.hkl.you import hkl
+
+Getting help
+------------
+
+To get help for the diffcalc angle calculations, the orientation phase, the
+angle calculation phase, and the dummy hardware adapter commands::
+
+ >>> help(dc)
+ >>> help(ub)
+ >>> help(hkl)
+ >>> help(hardware)
+
+
+Orientation
+-----------
+
+To orient the crystal for example (see the user manual for a fuller
+tutorial) first find some reflections::
+
+ # Create a new ub calculation and set lattice parameters
+ ub.newub('test')
+ ub.setlat('cubic', 1, 1, 1, 90, 90, 90)
+
+ # Add 1st reflection (demonstrating the hardware adapter)
+ hardware.settings.hardware.wavelength = 1
+ ub.c2th([1, 0, 0]) # energy from hardware
+ settings.hardware.position = 0, 60, 0, 30, 0, 0 # mu del nu eta chi ph
+ ub.addref([1, 0, 0]) # energy & pos from hardware
+
+ # Add 2nd reflection (this time without the hardware adapter)
+ ub.c2th([0, 1, 0], 12.39842)
+ ub.addref([0, 1, 0], [0, 60, 0, 30, 0, 90], 12.39842)
+
+
+To check the state of the current UB calculation::
+
+ >>> ub.ub()
+
+ UBCALC
+
+ name: test
+
+ n_phi: 0.00000 0.00000 1.00000 <- set
+ n_hkl: -0.00000 0.00000 1.00000
+ miscut: None
+
+ CRYSTAL
+
+ name: cubic
+
+ a, b, c: 1.00000 1.00000 1.00000
+ 90.00000 90.00000 90.00000
+
+ B matrix: 6.28319 0.00000 0.00000
+ 0.00000 6.28319 0.00000
+ 0.00000 0.00000 6.28319
+
+ UB MATRIX
+
+ U matrix: 1.00000 0.00000 0.00000
+ 0.00000 1.00000 0.00000
+ 0.00000 0.00000 1.00000
+
+ U angle: 0
+
+ UB matrix: 6.28319 0.00000 0.00000
+ 0.00000 6.28319 0.00000
+ 0.00000 0.00000 6.28319
+
+ REFLECTIONS
+
+ ENERGY H K L MU DELTA GAM ETA CHI PHI TAG
+ 1 12.398 1.00 0.00 0.00 0.0000 60.0000 0.0000 30.0000 0.0000 0.0000
+ 2 12.398 0.00 1.00 0.00 0.0000 60.0000 0.0000 30.0000 0.0000 90.0000
+
+And finally to check the reflections were specified acurately::
+
+ >>> dc.checkub()
+
+ ENERGY H K L H_COMP K_COMP L_COMP TAG
+ 1 12.3984 1.00 0.00 0.00 1.0000 0.0000 0.0000
+ 2 12.3984 0.00 1.00 0.00 -0.0000 1.0000 0.0000
+
+Motion
+------
+
+Hkl positions and virtual angles can now be read up from angle
+settings (the easy direction!)::
+
+
+ >>> dc.angles_to_hkl((0., 60., 0., 30., 0., 0.)) # energy from hardware
+
+ ((1.0, 5.5511151231257827e-17, 0.0),
+ {'alpha': -0.0,
+ 'beta': 3.5083546492674376e-15,
+ 'naz': 0.0,
+ 'psi': 90.0,
+ 'qaz': 90.0,
+ 'tau': 90.0,
+ 'theta': 29.999999999999996})
+
+Before calculating the settings to reach an hkl position (the trickier
+direction) hardware limits must be set and combination of constraints
+chosen. The constraints here result in a four circle like mode with a
+vertical scattering plane and incident angle 'alpha' equal to the exit
+angle 'beta'::
+
+ >>> hkl.con('qaz', 90)
+ ! 2 more constraints required
+ qaz: 90.0000
+
+ >>> hkl.con('a_eq_b')
+ ! 1 more constraint required
+ qaz: 90.0000
+ a_eq_b
+
+ >>> hkl.con('mu', 0)
+ qaz: 90.0000
+ a_eq_b
+ mu: 0.0000
+
+To check the constraints::
+
+ >>> hkl.con()
+ DET REF SAMP
+ ====== ====== ======
+ delta --> a_eq_b --> mu
+ alpha eta
+ --> qaz beta chi
+ naz psi phi
+ mu_is_nu
+
+ qaz: 90.0000
+ a_eq_b
+ mu: 0.0000
+
+ Type 'help con' for instructions
+
+Limits can be set to help Diffcalc choose a solution::
+
+ >>> hardware.setmin('delta', 0) # used when choosing solution
+
+Angles and virtual angles are then easily determined for a given hkl reflection::
+
+ >>> dc.hkl_to_angles(1, 0, 0) # energy from hardware
+ ((0.0, 60.0, 0.0, 30.0, 0.0, 0.0),
+ {'alpha': -0.0,
+ 'beta': 0.0,
+ 'naz': 0.0,
+ 'psi': 90.0,
+ 'qaz': 90.0,
+ 'tau': 90.0,
+ 'theta': 30.0}
+ )
diff --git a/script/__Lib/diffcalc-2.1/doc/source/developer/quickstart_setup_environment b/script/__Lib/diffcalc-2.1/doc/source/developer/quickstart_setup_environment
new file mode 100755
index 0000000..fe36a74
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/source/developer/quickstart_setup_environment
@@ -0,0 +1,25 @@
+
+Change directory to the diffcalc project (python adds the current
+working directory to the path)::
+
+ $ cd diffcalc
+ $ ls
+ COPYING diffcalc doc example mock.py mock.pyc model numjy test
+
+If using Python make sure numpy and diffcalc can be imported::
+
+ $ python
+ Python 2.7.2+ (default, Oct 4 2011, 20:06:09)
+ [GCC 4.6.1] on linux2
+ Type "help", "copyright", "credits" or "license" for more information.
+ >>> import numpy
+ >>> import diffcalc
+
+If using Jython make sure Jama and diffcalc can be imported::
+
+ $ jython -Dpython.path=:/Jama-1.0.1.jar
+
+ Jython 2.2.1 on java1.5.0_11
+ Type "copyright", "credits" or "license" for more information.
+ >>> import Jama
+ >>> import diffcalc
diff --git a/script/__Lib/diffcalc-2.1/doc/source/diffcalc_pdf.png b/script/__Lib/diffcalc-2.1/doc/source/diffcalc_pdf.png
new file mode 100755
index 0000000..7e08c66
Binary files /dev/null and b/script/__Lib/diffcalc-2.1/doc/source/diffcalc_pdf.png differ
diff --git a/script/__Lib/diffcalc-2.1/doc/source/diffcalc_web.png b/script/__Lib/diffcalc-2.1/doc/source/diffcalc_web.png
new file mode 100755
index 0000000..d7ddec5
Binary files /dev/null and b/script/__Lib/diffcalc-2.1/doc/source/diffcalc_web.png differ
diff --git a/script/__Lib/diffcalc-2.1/doc/source/index.rst b/script/__Lib/diffcalc-2.1/doc/source/index.rst
new file mode 100755
index 0000000..b65fbd4
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/source/index.rst
@@ -0,0 +1,26 @@
+###################################
+ Diffcalc User and Developer Guide
+###################################
+
+:Author: Rob Walton
+:Contact: rob.walton (at) diamond.ac.uk
+:Web site: https://github.com/DiamondLightSource/diffcalc
+
+.. rubric:: Diffcalc: A Diffraction Condition Calculator for Diffractometer Control
+
+See also the `quickstart guide at github `_.
+
+.. toctree::
+ :maxdepth: 2
+
+ youmanual
+ vliegmanual/contents
+ developer/contents
+ ACKS
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/contents.rst b/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/contents.rst
new file mode 100755
index 0000000..7d60a69
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/contents.rst
@@ -0,0 +1,22 @@
+#############################################
+Diffcalc User Guide (Deprecated Vlieg Engine)
+#############################################
+
+:Author: Rob Walton
+:Contact: rob.walton (at) diamond (dot) ac (dot) uk
+:Website: http://www.opengda.org/
+
+.. rubric:: Diffcalc: A diffraction condition calculator for diffractometer control
+
+.. toctree::
+ :maxdepth: 2
+ :numbered:
+
+ vliegmanual
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/fix.png b/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/fix.png
new file mode 100755
index 0000000..b4307d4
Binary files /dev/null and b/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/fix.png differ
diff --git a/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/sixcircle_gamma_on_arm.pdf b/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/sixcircle_gamma_on_arm.pdf
new file mode 100755
index 0000000..907a254
Binary files /dev/null and b/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/sixcircle_gamma_on_arm.pdf differ
diff --git a/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/sixcircle_gamma_on_arm.png b/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/sixcircle_gamma_on_arm.png
new file mode 100755
index 0000000..a1f2d93
Binary files /dev/null and b/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/sixcircle_gamma_on_arm.png differ
diff --git a/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/sixcircle_gamma_on_arm.ppt b/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/sixcircle_gamma_on_arm.ppt
new file mode 100755
index 0000000..0af759a
Binary files /dev/null and b/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/sixcircle_gamma_on_arm.ppt differ
diff --git a/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/unit_cell.pdf b/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/unit_cell.pdf
new file mode 100755
index 0000000..3114c35
Binary files /dev/null and b/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/unit_cell.pdf differ
diff --git a/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/unit_cell.png b/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/unit_cell.png
new file mode 100755
index 0000000..b431458
Binary files /dev/null and b/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/images/unit_cell.png differ
diff --git a/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/vliegmanual.rst b/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/vliegmanual.rst
new file mode 100755
index 0000000..4638b3e
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/source/vliegmanual/vliegmanual.rst
@@ -0,0 +1,827 @@
+Introduction
+============
+
+.. warning::
+
+ This manual refers to the 'Vlieg' calculation available in Diffcalc I. By
+ default Diffcalc II now uses its 'You' engine. This manual will be updated
+ soon. For now the developer guide shows how the new constraint system works.
+
+This manual assumes that you are running Diffcalc within the external
+framework of the GDA or Minigda and that Diffcalc has been configured
+for the six circle diffractometer pictured here:
+
+.. figure:: images/sixcircle_gamma_on_arm.*
+ :scale: 50
+ :align: center
+
+ Gamma-on-delta six-circle diffractometer, modified from Elias Vlieg
+ & Martin Lohmeier (1993)
+
+Your Diffcalc configuration will have been customised for the geometry
+of your diffractometer and possibly the types of experiment you
+perform. For example: a five-circle diffractometer might be missing
+the Gamma circle above, some six-circle modes and the option to fix
+gamma that would otherwise exist in some modes.
+
+The laboratory, crystal and reciprocal-lattice coordinate frames are
+defined with respect to the beam and to gravity to be (for a cubic crystal):
+
+.. figure:: images/fix.png
+ :align: center
+
+ Laboratory and illustratrive crystal coordinate frames for a cubic crystal
+
+The crystal lattice basis vectors are defined within the Cartesian
+crystal coordinate frame to be:
+
+.. figure:: images/unit_cell.*
+ :align: center
+ :scale: 100
+
+ Unit cell defined in crystal coordinate frame
+
+.. _overview:
+
+Overview
+========
+
+The following assumes that the diffractometer has been properly levelled, aligned with
+the beam and zeroed. See the `SPEC fourc manual `__.
+
+Before moving in hkl space you must calculate a UB matrix by
+specifying the crystal's lattice parameters (which define the B
+matrix) and finding two reflections (from which the
+U matrix can be inferred); and, optionally for surface-diffraction
+experiments, determine how the surface of the crystal is oriented with
+respect to the phi axis.
+
+
+Once a UB matrix has been calculated, the diffractometer may be driven
+in hkl coordinates. A valid diffractometer setting maps easily into a
+single hkl value. However for a diffractometer with more than three circles
+there are excess degrees of freedom when calculating a diffractometer
+setting from an hkl value. Diffcalc provides modes for using up
+the excess degrees of freedom.
+
+Diffcalc does not perform scans directly. Instead, scannables that use
+diffcalc to map between reciprocal lattice space and real
+diffractometer settings are scanned using the Gda's (or minigda's)
+generic scan mechanism.
+
+
+Theory
+------
+
+Thanks to Elias Vlieg for sharing his dos based ``DIF`` software that
+Diffcalc has borrowed heavily from. (See also the THANKS.txt file).
+
+See the papers (included in ``docs/ref``):
+
+* Busing & Levi (1966), "Angle Calculations for 3- and 4- Circle X-ray
+ and Neutron Diffractometers", Acta Cryst. 22, 457
+
+* Elias Vlieg & Martin Lohmeier (1993), "Angle Calculations for a Six-Circle
+ Surface X-ray Diffractometer", J. Appl. Cryst. 26, 706-716
+
+Getting Help
+============
+
+There are few commands to remember. If a command is called without
+arguments, Diffcalc will prompt for arguments and provide sensible
+defaults which can be chosen by pressing enter.
+
+The ``helpub`` and ``helphkl`` commands provide help with the crystal
+orientation and hkl movement phases of an experiment respectively::
+
+ >>> helpub
+
+ Diffcalc
+ --------
+ helpub ['command'] - lists all ub commands, or one if command is given
+ helphkl ['command'] - lists all hkl commands, or one if command is given
+
+ UB State
+ --------
+ newub 'name' - starts a new ub calculation with no lattice or
+ reflection list
+ loadub 'name' - loads an existing ub calculation: lattice and
+ reflection list
+ saveubas 'name' - saves the ubcalculation with a new name (other
+ changes autosaved)
+ ub - shows the complete state of the ub calculation
+
+ UB lattice
+ ----------
+ setlat - prompts user to enter lattice parameters (in
+ Angstroms and Deg.)
+ setlat 'name' a - assumes cubic
+ setlat 'name' a b - assumes tetragonal
+ setlat 'name' a b c - assumes ortho
+ setlat 'name' a b c gam - assumes mon/hex with gam not equal to 90
+ setlat 'name' a b c alpha beta gamma - arbitrary
+
+ UB surface
+ ----------
+ sigtau [sigma tau] - sets sigma and tau
+
+ UB reflections
+ --------------
+ showref - shows full reflection list
+ addref - add reflection
+ addref h k l ['tag'] - add reflection with hardware position and energy
+ addref h k l (p1,p2...pN) energy ['tag']- add reflection with specified position
+ and energy
+ delref num - deletes a reflection (numbered from 1)
+ swapref - swaps first two reflections used for calculating U
+ swapref num1 num2 - swaps two reflections (numbered from 1)
+
+ UB calculation
+ --------------
+ setu [((,,),(,,),(,,))] - manually set u matrix
+ setub ((,,),(,,),(,,)) - manually set ub matrix
+ calcub - (re)calculate u matrix from ref1 and ref2
+ checkub - show calculated and entered hkl values for reflections
+
+ >>> helphkl
+
+ Diffcalc
+ --------
+ helphkl [command] - lists all hkl commands, or one if command is given
+ helpub [command] - lists all ub commands, or one if command is given
+
+ Settings
+ --------
+ hklmode [num] - changes mode or shows current and available modes
+ and all settings
+ setalpha [num] - fixes alpha, or shows all settings if no num given
+ setgamma [num] - fixes gamma, or shows all settings if no num given
+ setbetain [num] - fixes betain, or shows all settings if no num given
+ setbetaout [num] - fixes betaout, or shows all settings if no num given
+ trackalpha [boolean] - determines wether alpha parameter will track alpha axis
+ trackgamma [boolean] - determines wether gamma parameter will track gamma axis
+ trackphi [boolean] - determines wether phi parameter will track phi axis
+ setsectorlim [omega_high omega_low phi_high phi_low]- sets sector limits
+
+ Motion
+ ------
+ pos hkl [h k l] - move diffractometer to hkl, or read hkl position.
+ Use None to hold a value still
+ sim hkl [h k l] - simulates moving hkl
+ hkl - shows loads of info about current hkl position
+ pos sixc [alpha, delta, gamma, omega, chi, phi,]- move diffractometer to Eularian
+ position. Use None to hold a
+ value still
+ sim sixc [alpha, delta, gamma, omega, chi, phi,]- simulates moving sixc
+ sixc - shows loads of info about current sixc position
+
+
+Diffcalc's Scannables
+=====================
+
+Please see :ref:`moving-in-hkl-space` and :ref:`scanning-in-hkl-space` for some relevant examples.
+
+To list and show the current positions of your beamline's scannables
+use ``pos`` with no arguments::
+
+ >>> pos
+
+Results in:
+
+**Energy and wavelength scannables**::
+
+ energy 12.3984
+ wl: 1.0000
+
+**Diffractometer scannables**, as a group and in component axes (in
+the real GDA these have limits)::
+
+ sixc: alpha: 0.0000 delta: 0.0000 gamma: 0.0000 omega: 0.0000 chi: 0.0000 phi: 0.0000
+ alpha: 0.0000
+ chi: 0.0000
+ delta: 0.0000
+ gamma: 0.0000
+ omega: 0.0000
+ phi: 0.0000
+
+**Dummy counter**, which in this example simply counts at 1hit/s::
+
+ cnt: 0.0000
+
+**Hkl scannable**, as a group and in component::
+
+ hkl: Error: No UB matrix
+ h: Error: No UB matrix
+ k: Error: No UB matrix
+ l: Error: No UB matrix
+
+**Parameter scannables**, used in some modes, these provide a
+scannable alternative to the series of ``fix`` commands described in
+:ref:`moving-in-hkl-space`.::
+
+ alpha_par:0.00000
+ azimuth: ---
+ betain: ---
+ betaout: ---
+ gamma_par:0.00000
+ phi_par: ---
+
+ Note that where a parameter corresponds with a physical
+ diffractometer axis, it can also be set to track that axis
+ directly. See `Tracking axis`_ below.
+
+Crystal orientation
+===================
+
+Before moving in hkl space you must calculate a UB matrix by
+specifying the crystal's lattice parameters (which define the B
+matrix) and finding two reflections (from which the
+U matrix can be inferred); and, optionally for surface-diffraction
+experiments, determine how the surface of the crystal is oriented with
+respect to the phi axis (see :ref:`overview`).
+
+Starting a UB calculation
+-------------------------
+
+A *UB-calculation* contains the description of the crystal-under-test,
+any saved reflections, sigma & tau (both default to 0), and a B & UB
+matrix pair if they have been calculated or manually specified.
+Starting a new UB calculation will clear all of these.
+
+Before starting a UB-calculation, the ``ub`` command used to summarise
+the state of the current UB-calculation, will reflect that no
+UB-calculation has been started::
+
+ >>> ub
+ No UB calculation started.
+ Wavelength: 1.239842
+ Energy: 10.000000
+
+A new UB-calculation calculation may be started and lattice specified
+explicitly::
+
+ >>> newub 'b16_270608'
+ >>> setlat 'xtal' 3.8401 3.8401 5.43072 90 90 90
+
+or interactively::
+
+ >>> newub
+ calculation name: b16_270608
+ crystal name: xtal
+ a [1]: 3.8401
+ b [3.8401]: 3.8401
+ c [3.8401]: 5.43072
+ alpha [90]: 90
+ beta [90]: 90
+ gamma [90]: 90
+
+where a,b and c are the lengths of the three unit cell basis vectors
+in Angstroms, and alpha, beta and gamma the typically used angles
+(defined in the figure above) in Degrees.
+
+The ``ub`` command will show the state of the current UB-calculation
+(and the current energy for reference)::
+
+ UBCalc: b16_270608
+ ======
+
+ Crystal
+ -------
+ name: xtal
+
+ lattice: a ,b ,c = 3.84010, 3.84010, 5.43072
+ alpha, beta , gamma = 90.00000, 90.00000, 90.00000
+
+ reciprocal: b1, b2, b3 = 1.63620, 1.63620, 1.15697
+ beta1, beta2, beta3 = 1.57080, 1.57080, 1.57080
+
+ B matrix: 1.6362035642769 -0.0000000000000 -0.000000000000
+ 0.0000000000000 1.6362035642769 -0.000000000000
+ 0.0000000000000 0.0000000000000 1.156970955450
+
+ Reflections
+ -----------
+ energy h k l alpha delta gamma omega chi phi tag
+
+ UB matrix
+ ---------
+ none calculated
+
+ Sigma: 0.000000
+ Tau: 0.000000
+ Wavelength: 1.000000
+ Energy: 12.398420
+
+
+Specifying Sigma and Tau for surface diffraction experiments
+------------------------------------------------------------
+Sigma and Tau are used in modes that fix either the beam exit or entry angle with
+respect to the crystal surface, or that keep the surface normal in the horizontal
+laboratory plane. For non surface-diffraction experiments these can
+safely be left at zero.
+
+For surface diffraction experiments, where not only the crystal's
+lattice planes must be oriented appropriately but so must the crystal's
+optical surface, two angles _Tau_ and _Sigma_ define the orientation of
+the surface with respect to the phi axis. Sigma is (minus) the amount of chi axis
+rotation and Tau (minus) the amount of phi axis rotation needed to
+move the surface normal parallel to the omega circle
+axis. These angles are often determined by reflecting a laser from the
+surface of the Crystal onto some thing and moving chi and tau until
+the reflected spot remains stationary with movements of omega.
+
+Use ``sigtau`` with no args to set interactively::
+
+ >>> pos chi -3.1
+ chi: -3.1000
+ >>> pos phi 10.0
+ phi: 10.0000
+ >>> sigtau
+ sigma, tau = 0.000000, 0.000000
+ chi, phi = -3.100000, 10.000000
+ sigma[ 3.1]: 3.1
+ tau[-10.0]: 10.0
+
+Sigma and Tau can also be set explicitly::
+
+ >>>sigtau 0 0
+
+
+Managing reflections
+--------------------
+The normal way to calculate a UB matrix is to find the position of **two**
+reflections with known hkl values. Diffcalc allows many
+reflections to be recorded but currently only uses the first two when
+calculating a UB matrix.
+
+Add reflection at current location
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It is normal to first move to a reflection::
+
+ >>> pos en 10
+ en: 10.0000
+ >>> pos sixc [5.000, 22.790, 0.000, 1.552, 22.400, 14.255]
+ sixc: alpha: 5.0000 delta: 22.7900 gamma: 0.0000 omega: 1.5520 chi: 22.4000 phi: 14.2550
+
+
+and then use the ``addref`` command either explicitly::
+
+ addref 1 0 1.0628 'optional_tag'
+
+or interactively::
+
+ >>> addref
+ h: 1
+ k: 0
+ l: 1.0628
+ current pos[y]: y
+ tag: 'tag_string'
+
+to add a reflection.
+
+Add a reflection manually
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If a reflection cannot be reached but its position is known (or if its
+position has been previously determined), a reflection may be added
+without first moving to it either explicitly::
+
+ >>> addref 0 1 1.0628 [5.000, 22.790, 0.000,4.575, 24.275, 101.320] 'optional_tag'
+
+or interactively::
+
+ >>> addref
+ h: 0
+ k: 1
+ l: 1.0628
+ current pos[y]: n
+ alpha[5.000]:
+ delta[22.79]:
+ gamma[0.000]:
+ omega[1.552]: 4.575
+ chi[22.40]: 24.275
+ phi[14.25]: 101.320
+ en[9.998]:
+ tag: optional_tag2
+
+Edit reflection list
+~~~~~~~~~~~~~~~~~~~~
+
+Use ``showref`` to show the reflection list::
+
+ >>> showref
+ energy h k l alpha delta gamma omega chi phi tag
+ 1 9.999 1.00 0.00 1.06 5.0000 22.7900 0.0000 1.5520 22.4000 14.2550 1st
+ 2 9.999 0.00 1.00 1.06 5.0000 22.7900 0.0000 4.5750 24.2750 101.32000 2nd
+
+Use ``swapref`` to swap reflections::
+
+ >>> swapref 1 2
+ Recalculating UB matrix.
+ >>> showref
+ energy h k l alpha delta gamma omega chi phi tag
+ 1 9.999 0.00 1.00 1.06 5.0000 22.7900 0.0000 4.5750 24.2750 101.3200 2nd
+ 2 9.999 1.00 0.00 1.06 5.0000 22.7900 0.0000 1.5520 22.4000 14.2550 1st
+
+Use ``delref`` to delete a reflection::
+
+ >>> delref 1
+ >>> showref
+ energy h k l alpha delta gamma omega chi phi tag
+ 1 9.999 1.00 0.00 1.06 5.0000 22.7900 0.0000 1.5520 22.4000 14.2550 1st
+
+Calculating a UB matrix
+-----------------------
+
+Unless a U or UB matrix has been manually specified, a new UB matrix
+will be calculated after the second reflection has been found, or
+whenever one of the first two reflections is changed.
+
+Use the command ``calcub`` to force the UB matrix to be calculated
+from the first two reflections.
+
+If you have misidentified a reflection used for the orientation the
+resulting UB matrix will be incorrect. Always use the ``checkub``
+command to check that the computed values agree with the estimated values::
+
+ >>>checkub
+ energy h k l h_comp k_comp l_comp tag
+ 1 9.9987 1.00 0.00 1.06 1.0000 0.0000 1.0628 1st
+ 2 9.9987 0.00 1.00 1.06 -0.0329 1.0114 1.0400 2nd
+
+Notice that the first reflection will always match, but that the
+second will not match exactly. (The system of equations used to
+calculate the U matrix is overdetermined and some information from the
+second reflection is thrown away.)
+
+Manually setting U and UB
+-------------------------
+
+*To help find the initial reflections* it may be useful to set the U
+matrix manually---to the identity matrix for example. Use the ``setu``
+command to do this. Once set the diffractometer may be driven to the
+ideal location of a reflection and then the actual reflection
+sought. Normally this would be done in the default mode, four-circle-bisecting, (see
+:ref:`moving-in-hkl-space`). In the following example this has been done
+by setting the alpha to 5 and leaving gamma at 0 (it would be normal
+to leave alpha at 0)::
+
+ >>> hklmode 1
+ 1) fourc bisecting
+ alpha: 0.0
+ gamma: 0.0
+
+ >>> setalpha 5
+ alpha: 0 --> 5.000000
+ >>> setu
+ row1[1 0 0]:
+ row2[0 1 0]:
+ row3[0 0 1]:
+ >>> sim hkl [1,0,1.0628] # Check it all makes sense
+ sixc would move to:
+ alpha : 5.00000 deg
+ delta : 22.79026 deg
+ gamma : 0.00000 deg
+ omega : 5.82845 deg
+ chi : 24.57658 deg
+ phi : 6.14137 deg
+
+ theta : 70702.991919
+ 2theta : 23.303705
+ Bin : 6.969151
+ Bout : 6.969151
+ azimuth : 7.262472
+
+ >>> pos hkl [1,0,1.0628]
+ hkl: h: 1.00000 k: 0.00000 l: 1.06280
+
+ >>> # scan about to find actual reflection
+
+ >>> addref
+ h[0.0]: 1
+ k[0.0]: 0
+ l[0.0]: 1.0628
+ current pos[y]: y
+ tag: 'ref1'
+ >>>
+
+
+There is currently no way to refine a manually specified U matrix by
+inferring as much as possible from just one found reflection.
+
+.. _moving-in-hkl-space:
+
+Moving in hkl space
+===================
+
+Once a UB matrix has been calculated, the diffractometer may be driven
+in hkl coordinates. A given diffractometer setting maps easily into a
+single hkl value. However for a diffractometer with more than three circles
+there are excess degrees of freedom when calculating a diffractometer
+setting from an hkl value. Diffcalc provides many for using up
+the excess degrees of freedom.
+
+By default Diffcalc selects four-circle bisecting mode (see below).
+
+Note that to play along with the following ``run`` the file in
+``example/session/sixc_example.py`` to configure the UB-calculation.
+
+
+Modes
+-----
+
+Use the command ``hklmode`` to summarise the state of Diffcalc's angle
+calculator. It shows a list the available modes for your
+diffractometer and the parameters that must be fixed for each, the
+current mode and the current parameter settings::
+
+ >>> hklmode
+ Available modes:
+ 0) fourc fixed-bandlw (alpha, gamma, blw) (Not impl.)
+ 1) fourc bisecting (alpha, gamma)
+ 2) fourc incoming (alpha, gamma, betain)
+ 3) fourc outgoing (alpha, gamma, betaout)
+ 4) fourc azimuth (alpha, gamma, azimuth) (Not impl.)
+ 5) fourc fixed-phi (alpha, gamma, phi) (Not impl.)
+ 10) fivec bisecting (gamma)
+ 11) fivec incoming (gamma, betain)
+ 12) fivec outgoing (gamma, betaout)
+ 13) fivec bisecting (alpha)
+ 14) fivec incoming (alpha, betain)
+ 15) fivec outgoing (alpha, betaout)
+ 20) zaxis bisecting ()
+ 21) zaxis incoming (betain)
+ 22) zaxiz outgoing (betaout)
+
+ Current mode:
+
+ 1) fourc bisecting
+ Parameters:
+
+ alpha: 0.0
+ gamma: 0.0
+ betain: --- (not relevant in this mode)
+ betaout: --- (not relevant in this mode)
+ azimuth: --- (not relevant in this mode)
+ phi: --- (not relevant in this mode)
+ blw: --- (not relevant in this mode)
+
+Note that 'Not impl.' is short for 'not implemented'. Standby.
+
+Your output may differ. For example:
+
+ - When listed with a typical five-circle diffractometer with no gamma
+ circle: the fourc modes will have no gamma parameter to fix
+ (actually it will have been fixed under the covers to 0), there
+ will be no gamma or alpha parameters to fix in the five circle
+ modes (again, under the covers gamma will have been fixed) and
+ there will be no zaxis modes (as these require six circles, or an
+ actual z-axis diffractometer).
+
+ - When listed with a typical four-circle diffractometer with no alpha
+ or gamma circle, the four-circle modes will appear with no alpha or
+ gamma parameters (again, they are fixed under the covers), and
+ there will be no five circle or zaxis modes.
+
+To change the current mode, call ``hklmode`` with an argument::
+
+ >>> hklmode 2
+ 2) fourc incoming
+ alpha: 0.0
+ gamma: 0.0
+ betain: ---
+
+(The dashes next to the betain parameter indicate that a parameter
+has not yet been set.)
+
+Mode parameters
+---------------
+
+A parameter can be set using either one of the series of {{{set}}}
+commands, by moving one of the scannables associated with each
+parameter or, where appropriate, by asking that a parameter track an
+axis.
+
+Set commands
+~~~~~~~~~~~~
+Use the series of commands ``set`` to set a parameter::
+
+ >>> setalpha 3
+ alpha: 0 --> 3.000000
+ >>> setbetain 5
+ WARNING: The parameter betain is not used in mode 1
+ betain: --- --> 5.000000
+ >>> setalpha # With no args, the current value is displayed
+ alpha: 3
+ >>> setbetain
+ betain: ---
+
+
+Parameter Scannables
+~~~~~~~~~~~~~~~~~~~~
+
+In most installations there will be a scannable for each parameter. In
+this example installation, the parameters which correspond to physical
+axes have had '_par' appended to their names to prevent clashes. These
+may be used to change a parameter either with the ``pos`` command or
+by using them within a scan (see :ref:`scanning-in-hkl-space`).::
+
+ >>> pos betain
+ betain: 0.00000
+ >>> pos betain 5
+ betain: 5.00000
+ >>> setbetain
+ betain: 5
+
+ >>> pos alpha_par
+ alpha_par:3.00000
+ >>> setalpha
+ alpha: 3
+
+
+Tracking Axis
+~~~~~~~~~~~~~
+Where a parameter matches an axis name, that parameter may be set to
+track that axis::
+
+ >>> pos alpha
+ alpha: 5.0000
+
+ >>> hklmode 1
+ 1) fourc bisecting
+ alpha: 0.0
+ gamma: 0.0
+
+ >>> trackalpha
+ alpha: 5
+
+ >>> pos alpha
+ alpha: 6.0000
+
+ >>> hklmode 1
+ 1) fourc bisecting
+ alpha: 6.0 (tracking physical axis)
+ gamma: 0.0
+
+
+Although convenient, there is a danger with this method that in
+geometries where the axes are built from other axes (such as in a
+kappa geometry), the position of an axis may drift slightly during a
+scan.
+
+Sectors
+-------
+
+When mapping from reciprocal lattice space to a set of diffractometer
+settings, there is normally a choice of solutions for the sample
+orientation. The selected sector mode will determine which solution is
+used. There is currently only one sector mode:
+
+Sector mode: Find first solution within sector limits
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In this sector mode, taken from 'DIF', the first solution found within
+the 'sector limits' is chosen. These are different from the physical
+or software limits on the axes and can be checked/modified using
+``setsectorlim``::
+
+ >>> setsectorlim
+ omega_high[270]:
+ omega_low[-90]:
+ phi_high[180]:
+ phi_low[-180]:
+
+
+The hkl scannable
+-----------------
+Once a UB matrix has been calculated, a mode chosen and parmeters set,
+use the hkl scannable to move to a point in reciprocal lattice space::
+
+ >>> pos hkl [1,0,0]
+ hkl: h: 1.00000 k: -0.00000 l: -0.00000
+ >>> pos sixc
+ sixc: alpha: 3.0000 delta: 17.2252 gamma: 4.0000 omega: 7.5046 chi: -24.6257 phi: 4.8026
+ >>> pos hkl
+ hkl: h: 1.00000 k: -0.00000 l: -0.00000
+ >>> hkl
+ hkl:
+ h : 1.000000
+ k : -0.000000
+ l : -0.000000
+ 2theta : 18.582618
+ Bin : -0.387976
+ Bout : -0.387976
+ azimuth : 1.646099
+
+Notice that typing ``hkl`` will also display some virtual angles (such
+as twotheta and Bin), that checking the position with ``pos hkl`` will
+not.
+
+To get this extra information into a scan use the scannable hklverbose
+instead of hkl::
+
+ >>> pos hklverbose [1,0,0]
+ hklverbose: h: 1.00000 k: -0.00000 l: -0.00000 2theta : 18.582618 Bin : -0.387976
+ Bout :-0.387976 azimuth : 1.646099
+
+The ``sim`` command will report, without moving the diffractometer,
+where an hkl position would be found::
+
+ >>> sim hkl [1,0,0]
+ sixc would move to:
+ alpha : 3.00000 deg
+ delta : 17.22516 deg
+ gamma : 4.00000 deg
+ omega : 7.50461 deg
+ chi : -24.62568 deg
+ phi : 4.80260 deg
+
+ theta : 70702.991919
+ 2theta : 18.582618
+ Bin : -0.387976
+ Bout : -0.387976
+ azimuth : 1.646099
+
+
+
+Moving out of range
+~~~~~~~~~~~~~~~~~~~
+Not every hkl position can be reached::
+
+ >>> pos hkl [10,10,10]
+ Exception: Could not compute delta for this hkl position
+
+The diffractometer scannable (sixc)
+-----------------------------------
+We've seen this before, but it also works with sim::
+
+ gda>>>sim sixc [3, 17.22516, 4, 7.50461, -24.62568, 4.80260]
+ hkl would move to:
+ h : 1.000000
+ k : 0.000000
+ l : -0.000000
+
+.. _scanning-in-hkl-space:
+
+Scanning in hkl space
+=====================
+
+All scans described below use the same generic scanning mechanism
+provided by the GDA system or by minigda. Here are some examples.
+
+Fixed hkl scans
+---------------
+
+In a 'fixed hkl scan' something (such as energy or Bin) is scanned,
+and at each step hkl is 'moved' to keep the sample and detector
+aligned. Also plonk the diffractometer scannable (sixc) on there with no
+destination to monitor what is actually happening and then
+throw on a detector (cnt) with an exposure time if appropriate::
+
+ >>> #scan scannable_name start stop step [scannable_name [pos or time]]..
+
+ >>> scan en 9 11 .5 hkl [1,0,0] sixc cnt 1
+
+ >>> scan en 9 11 .5 hklverbose [1,0,0] sixc cnt 1
+
+ >>> scan betain 4 5 .2 hkl [1,0,0] sixc cnt 1
+
+ >>> scan alpha_par 0 10 2 hkl [1,0,0] sixc cnt 1
+
+ >>> trackalpha
+ >>> scan alpha 0 10 2 hkl [1,0,0] sixc cnt 1 # Equivalent to last scan
+
+Scanning hkl
+------------
+
+Hkl, or one component, may also be scanned directly::
+
+ >>> scan h .8 1.2 .1 hklverbose sixc cnt 1
+
+At each step, this will read the current hkl position, modify the h
+component and then move to the resulting vector. There is a danger
+that with this method k and l may drift. To get around this the start,
+stop and step values may also be specified as vectors. So for example::
+
+ >>> scan hkl [1,0,0] [1,.3,0] [1,0.1,0] cnt1
+
+is equivilant to::
+
+ >>> pos hkl [1,0,0]
+ >>> scan k 0 .3 .1 cnt1
+
+but will not suffer from drifting. This method also allows scans along
+any direction in hkl space to be performed.
+
+Multidimension scans
+--------------------
+
+Two and three dimensional scans::
+
+ >>> scan en 9 11 .5 h .9 1.1 .2 hklverbose sixc cnt 1
+ >>> scan h 1 3 1 k 1 3 1 l 1 3 1 hkl cnt 1
+
+
+
+Good luck --- RobW
diff --git a/script/__Lib/diffcalc-2.1/doc/source/youmanual.rst b/script/__Lib/diffcalc-2.1/doc/source/youmanual.rst
new file mode 100755
index 0000000..a6f1b30
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/source/youmanual.rst
@@ -0,0 +1,888 @@
+################################
+Diffcalc User Guide (You Engine)
+################################
+
+.. rubric:: Diffcalc: A diffraction condition calculator for diffractometer control
+
+:Author: Rob Walton
+:Contact: rob.walton (at) diamond (dot) ac (dot) uk
+:Website: https://github.com/DiamondLightSource/diffcalc
+
+.. toctree::
+ :maxdepth: 2
+ :numbered:
+
+See also the `quickstart guide at github `_
+
+Introduction
+============
+
+This manual assumes that you are running Diffcalc within OpenGDA or have started
+it using IPython. It assumes that Diffcalc has been configured for the six
+circle diffractometer pictured here:
+
+.. figure:: youmanual_images/4s_2d_diffractometer.png
+ :scale: 100
+ :align: center
+
+ 4s + 2d six-circle diffractometer, from H.You (1999)
+
+Your Diffcalc configuration may have been customised for the geometry of your
+diffractometer and possibly the types of experiment you perform. For example, a
+five-circle diffractometer might be missing the nu circle above.
+
+The laboratory frame is shown above. With all settings at zero as shown the
+crystal cartesian frame aligns with the laboratory frame. Therefor a cubic
+crystal mounted squarely in a way that the U matrix (defined below) is unitary
+will have h||a||x, k||b||y & l||c||z, crystal and reciprocal-lattice coordinate
+frames are defined with respect to the beam and to gravity to be (for a cubic
+crystal):
+
+Overview
+========
+
+The following assumes that the diffractometer has been properly leveled, aligned
+with the beam and zeroed. See the `SPEC fourc manual
+`__.
+
+Before moving in hkl space you must calculate a UB matrix by specifying the
+crystal's lattice parameters (which define the B matrix) and finding two
+reflections (from which the U matrix defining any mismount can be inferred);
+and, optionally for surface-diffraction experiments, determine how the surface
+of the crystal is oriented with respect to the phi axis.
+
+Once a UB matrix has been calculated, the diffractometer may be driven in hkl
+coordinates. A valid diffractometer setting maps easily into a single hkl value.
+However for a diffractometer with more than three circles there are excess
+degrees of freedom when calculating a diffractometer setting from an hkl value.
+Diffcalc provides modes for using up the excess degrees of freedom.
+
+Diffcalc does not perform scans directly. Instead, Scannables that use diffcalc
+to map between reciprocal lattice space and real diffractometer settings are
+scanned using the Gda's (or minigda's) generic scan mechanism.
+
+
+Theory
+------
+
+Thanks to Elias Vlieg for sharing his dos based ``DIF`` software that Diffcalc
+has borrowed heavily from. The version of Diffcalc described here is based on papers by
+pHH. You. [You1999]_ and Busing & Levy [Busing1967]_. (See also the THANKS.txt file.)
+
+Getting Help
+============
+
+There are few commands to remember. If a command is called without
+arguments in some cases Diffcalc will prompt for arguments and provide sensible
+defaults which can be chosen by pressing enter.
+
+
+**Orientation**. The ``helpub`` command lists all commands related with crystal
+orientation and the reference vector (often used with surfaces). See the
+`Orientation Commands`_ section at the end of this manual::
+
+ >>> help ub
+ ...
+
+
+**HKL movement**. The ``help hkl`` list all commands related to moving in reciprocal-lattice
+space. See the `Motion Commands`_ section at the end of this manual::
+
+ >>> help hkl
+ ...
+
+
+Call help on any command. e.g.::
+
+ >>> help loadub
+ loadub (diffcalc command):
+ loadub 'name' | num -- load an existing ub calculation
+
+Diffcalc's Scannables
+=====================
+
+To list and show the current positions of your beamline's scannables
+use ``pos`` with no arguments::
+
+ >>> pos
+
+Results in:
+
+**Energy and wavelength scannables**::
+
+ energy 12.3984
+ wl: 1.0000
+
+**Diffractometer scannables**, as a group and in component axes (in
+the real GDA these have limits)::
+
+ sixc: mu: 0.0000 delta: 0.0000 gamma: 0.0000 omega: 0.0000 chi: 0.0000 phi: 0.0000
+ mu: 0.0000
+ chi: 0.0000
+ delta: 0.0000
+ gamma: 0.0000
+ omega: 0.0000
+ phi: 0.0000
+
+**Dummy counter**, which in this example simply counts at 1hit/s::
+
+ ct: 0.0000
+
+**Hkl scannable**, as a group and in component::
+
+ hkl: Error: No UB matrix
+ h: Error: No UB matrix
+ k: Error: No UB matrix
+ l: Error: No UB matrix
+
+**Parameter scannables**, used in some modes, these provide a
+scannable alternative to the `Motion`_ section. Some constrain of
+these constrain virtual angles::
+
+ alpha: ---
+ beta: ---
+ naz: ---
+ psi: ---
+ qaz: ---
+
+and some constrain physical angles::
+
+ phi_con: ---
+ chi_con: ---
+ delta_con:---
+ eta_con: ---
+ gam_con: ---
+ mu_con: ---
+
+
+Crystal orientation
+===================
+
+Before moving in hkl space you must calculate a UB matrix by specifying the
+crystal's lattice parameters (which define the B matrix) and finding two
+reflections (from which the U matrix can be inferred); and, optionally for
+surface-diffraction experiments, determine how the surface of the crystal is
+oriented with respect to the phi axis.
+
+Start a new UB calculation
+--------------------------
+
+A *UB calculation* contains the description of the crystal-under-test,
+any saved reflections, reference angle direction, and a B & UB
+matrix pair if they have been calculated or manually specified.
+Starting a new UB calculation will clear all of these.
+
+Before starting a UB-calculation, the ``ub`` command used to summarise
+the state of the current UB-calculation, will reflect that no
+UB-calculation has been started::
+
+ >>> ub
+ <<< No UB calculation started >>>
+
+A new UB-calculation calculation may be started and lattice specified
+explicitly::
+
+ >>> newub 'example'
+ >>> setlat '1Acube' 1 1 1 90 90 90
+
+or interactively::
+
+ >>> newub
+ calculation name: example
+ crystal name: 1Acube
+ a [1]: 1
+ b [1]: 1
+ c [1]: 1
+ alpha [90]: 90
+ beta [90]: 90
+ gamma [90]: 90
+
+where a,b and c are the lengths of the three unit cell basis vectors
+in Angstroms, and alpha, beta and gamma are angles in Degrees.
+
+The ``ub`` command will show the state of the current UB-calculation
+(and the current energy for reference)::
+
+ >>> ub
+ UBCALC
+
+ name: example
+
+ n_phi: 0.00000 0.00000 1.00000 <- set
+
+ CRYSTAL
+
+ name: 1Acube
+
+ a, b, c: 1.00000 1.00000 1.00000
+ 90.00000 90.00000 90.00000
+
+ B matrix: 6.28319 0.00000 0.00000
+ 0.00000 6.28319 0.00000
+ 0.00000 0.00000 6.28319
+
+ UB MATRIX
+
+ <<< none calculated >>>
+
+ REFLECTIONS
+
+ <<< none specified >>>
+
+ CRYSTAL ORIENTATIONS
+
+ <<< none specified >>>
+
+Load a UB calculation
+---------------------
+
+To load the last used UB-calculation::
+
+ >>> lastub
+ Loading ub calculation: 'mono-Si'
+
+To load a previous UB-calculation::
+
+ >>> listub
+ UB calculations in: /Users/walton/.diffcalc/i16
+
+ 0) mono-Si 15 Feb 2017 (22:32)
+ 1) i16-32 13 Feb 2017 (18:32)
+
+ >>> loadub 0
+
+Generate a U matrix from two reflections
+----------------------------------------
+
+The normal way to calculate a U matrix is to find the position of **two**
+reflections with known hkl values. Diffcalc allows many reflections to be
+recorded but currently only uses the first two when calculating a UB matrix.
+
+Find U matrix from two reflections::
+
+ >>> pos wl 1
+ wl: 1.0000
+ >>> c2th [0 0 1]
+ 59.99999999999999
+
+ >>> pos sixc [0 60 0 30 90 0]
+ sixc: mu: 0.0000 delta: 60.0000 gam: 0.0000 eta: 30.0000 chi: 90.0000 phi: 0.0000
+ >>> addref [0 0 1]
+
+ >>> pos sixc [0 90 0 45 45 90]
+ sixc: mu: 0.0000 delta: 90.0000 gam: 0.0000 eta: 45.0000 chi: 45.0000 phi: 90.0000
+ >>> addref [0 1 1]
+ Calculating UB matrix.
+
+Check that it looks good::
+
+ >>> checkub
+
+ ENERGY H K L H_COMP K_COMP L_COMP TAG
+ 1 12.3984 0.00 0.00 1.00 0.0000 0.0000 1.0000
+ 2 12.3984 0.00 1.00 1.00 0.0000 1.0000 1.0000
+
+Generate a U matrix from one reflection
+---------------------------------------
+
+To estimate based on first reflection only::
+
+ >>> trialub
+ resulting U angle: 0.00000 deg
+ resulting U axis direction: [-1.00000, 0.00000, 0.00000]
+ Recalculating UB matrix from the first reflection only.
+ NOTE: A new UB matrix will not be automatically calculated when the orientation reflections are modified.
+
+Edit reflection list
+--------------------
+
+Use ``showref`` to show the reflection list::
+
+ >>> showref
+ ENERGY H K L MU DELTA GAM ETA CHI PHI TAG
+ 1 12.398 0.00 0.00 1.00 0.0000 60.0000 0.0000 30.0000 90.0000 0.0000
+ 2 12.398 0.00 1.00 1.00 0.0000 90.0000 0.0000 45.0000 45.0000 90.0000
+
+Use ``swapref`` to swap reflections::
+
+ >>> swapref 1 2
+ Not calculating UB matrix as it has been manually set. Use 'calcub' to explicitly recalculate it.
+ Recalculating UB matrix.
+
+Use ``delref`` to delete a reflection::
+
+ >>> delref 1
+
+Generate a U matrix from two lattice directions
+-----------------------------------------------
+
+Another approach to calculate a U matrix is to provide orientation of **two** crystal lattice
+directions in laboratory frame of reference using ``addorient`` command. The first lattice
+direction will be aligned along the specified in the laboratory frame. The second lattice
+direction will be used to set azimuthal orientation of the crystal in the plane perpendicular
+to the first lattice orientation. Diffcalc allows many lattice directions to be recorded but
+currently uses only the first two when calculating a UB matrix.
+
+Find U matrix from two lattice directions::
+
+ >>> addorient [0 0 1] [0 0 1]
+
+ >>> addorient [1 0 0] [1 1 0]
+ Calculating UB matrix.
+
+Calculate a UB matrix
+---------------------
+
+Unless a U or UB matrix has been manually specified, a new UB matrix will be
+calculated after the second reflection has been found, or whenever one of the
+first two reflections is changed.
+
+Use the command ``calcub`` to force the UB matrix to be calculated from the
+first two reflections. In case of using lattice orientations instead of reflections,
+use command ``orientub`` to force the UB matrix to be calculated from the first two orientations.
+
+If you have misidentified a reflection used for the orientation the
+resulting UB matrix will be incorrect. Always use the ``checkub``command
+to check that the computed reflection indices agree with the estimated values::
+
+ >>> checkub
+
+ ENERGY H K L H_COMP K_COMP L_COMP TAG
+ 1 12.3984 0.00 1.00 1.00 0.0000 1.0000 1.0000
+ 2 12.3984 0.00 0.00 1.00 0.0000 0.0000 1.0000
+
+Calculate a U matrix from crystal mismount
+-------------------------------------------
+
+U matrix can be defined from crystal mismount by using a rotation matrix calculated from a provided
+mismount angle and axis. ``setmiscut`` command defines new U matrix by setting it to a rotation matrix
+calculated from the specified angle and axis parameters. ``addmiscut`` command applies the calculated
+rotation matrix to the existing U matrix, i.e. adds extra mismount to the already existing one::
+
+ >>> setmiscut 5 [1 0 0]
+ n_phi: -0.00000 -0.08716 0.99619
+ n_hkl: 0.00000 0.00000 1.00000 <- set
+ normal:
+ angle: 5.00000
+ axis: 1.00000 -0.00000 0.00000
+
+
+Manually specify U matrix
+-------------------------
+
+Set U matrix manually (pretending sample is squarely mounted)::
+
+ >>> setu [[1 0 0] [0 1 0] [0 0 1]]
+ Recalculating UB matrix.
+ NOTE: A new UB matrix will not be automatically calculated when the orientation reflections are modified.
+
+Refining UB matrix from reflection
+----------------------------------
+
+UB matrix elements can be refined to match diffractometer settings and crystal orientation experimentally
+found for a given reflection with the corresponding reflection indices. ``refineub`` command rescales
+crystal unit cell dimensions to match with the found scattering angle value and recalculates mismount
+parameters to update U matrix::
+
+ >>> refineub [1 0 0]
+ current pos[y]: y
+ Unit cell scaling factor: 0.99699
+ Refined crystal lattice:
+ a, b, c: 0.99699 0.99699 0.99699
+ 90.00000 90.00000 90.00000
+
+ Update crystal settings?[y]: y
+ Warning: the old UB calculation has been cleared.
+ Use 'calcub' to recalculate with old reflections or
+ 'orientub' to recalculate with old orientations.
+ Miscut parameters:
+ angle: 2.90000
+ axis: -0.00000 1.00000 -0.00000
+ Apply miscut parameters?[y]: y
+ n_phi: 0.67043 -0.00000 0.74198
+ n_hkl: 0.00000 0.00000 1.00000 <- set
+ normal:
+ angle: 42.10000
+ axis: 0.00000 1.00000 0.00000
+
+Set the reference vector
+-------------------------
+
+When performing surface experiments the reference vector should be set normal
+to the surface. It can also be used to define other directions within the crystal
+with which we want to orient the incident or diffracted beam.
+
+By default the reference vector is set parallel to the phi axis. That is,
+along the z-axis of the phi coordinate frame.
+
+The `ub` command shows the current reference vector along with the orientation relative to
+the z-axis, at the top its report (or it can be shown by calling ``setnphi`` or
+``setnhkl'`` with no args)::
+
+ >>> ub
+ ...
+ n_phi: 0.00000 0.00000 1.00000 <- set
+ n_hkl: -0.00000 0.00000 1.00000
+ normal: None
+ ...
+
+The ``<- set`` label here indicates that the reference vector is set in the phi
+coordinate frame. In this case, therefore, its direction in the crystal's
+reciprocal lattice space is inferred from the UB matrix.
+
+To set the reference vector in the phi coordinate frame use::
+
+ >>> setnphi [0 0 1]
+ ...
+
+This is useful if the surface normal has be found with a laser or by x-ray
+occlusion. This vector must currently be manually calculated from the sample
+angle settings required to level the surface (sigma and tau commands on the
+way).
+
+To set the reference vector in the crystal's reciprocal lattice space use (this
+is a quick way to determine the surface orientation if the surface is known to
+be cleaved cleanly along a known axis)::
+
+ >>> setnhkl [0 0 1]
+ ...
+
+Motion
+======
+
+Once a UB matrix has been calculated, the diffractometer may be driven
+in hkl coordinates. A given diffractometer setting maps easily into a
+single hkl value. However for a diffractometer with more than three circles
+there are excess degrees of freedom when calculating a diffractometer
+setting from an hkl value. Diffcalc provides many for using up
+the excess degrees of freedom.
+
+By default Diffcalc selects no mode.
+
+Constraining solutions for moving in hkl space
+----------------------------------------------
+
+To get help and see current constraints::
+
+ >>> help con
+ ...
+
+ >>> con
+ DET REF SAMP
+ ------ ------ ------
+ delta a_eq_b mu
+ gam alpha eta
+ qaz beta chi
+ naz psi phi
+ mu_is_gam
+
+ ! 3 more constraints required
+
+ Type 'help con' for instructions
+
+Three constraints can be given: zero or one from the DET and REF columns and the
+remainder from the SAMP column. Not all combinations are currently available.
+Use ``help con`` to see a summary if you run into troubles.
+
+To configure four-circle vertical scattering::
+
+ >>> con gam 0 mu 0 a_eq_b
+ gam : 0.0000
+ a_eq_b
+ mu : 0.0000
+
+In the following the *scattering plane* is defined as the plane including the
+scattering vector, or momentum transfer vector, and the incident beam.
+
+**DETECTOR COLUMN:**
+
+- **delta** - physical delta setting (vertical detector motion) *del=0 is equivalent to qaz=0*
+- **gam** - physical gamma setting (horizontal detector motion) *gam=0 is equivalent to qaz=90*
+- **qaz** - azimuthal rotation of scattering vector (about the beam, from horizontal)
+- **naz** - azimuthal rotation of reference vector (about the beam, from horizontal)
+
+**REFERENCE COLUMN:**
+
+- **alpha** - incident angle to surface (if reference is normal to surface)
+- **beta** - exit angle from surface (if reference is normal to surface)
+- **psi** - azimuthal rotation about scattering vector of reference vector (from scattering plane)
+- **a_eq_b** - bisecting mode with alpha=beta. *Equivalent to psi=90*
+
+**SAMPLE COLUMN:**
+
+- **mu, eta, chi & phi** - physical settings
+- **mu_is_gam** - force mu to follow gamma (results in a 5-circle geometry)
+
+Diffcalc will report two other (un-constrainable) virtual angles:
+
+- **theta** - half of 2theta, the angle through the diffracted beam bends
+- **tau** - longitude of reference vector from scattering vector (in scattering plane)
+
+Example constraint modes
+------------------------
+
+There is sometimes more than one way to get the same effect.
+
+**Vertical four-circle mode**::
+
+ >>> con gam 0 mu 0 a_eq_b # or equivalently:
+ >>> con qaz 90 mu 0 a_eq_b
+
+ >>> con alpha 1 # replaces a_eq_b
+
+**Horizontal four-circle mode**::
+
+ >>> con del 0 eta 0 alpha 1 # or equivalently:
+ >>> con qaz 0 mu 0 alpha 1
+
+**Surface vertical mode**::
+
+ >>> con naz 90 mu 0 alpha 1
+
+**Surface horizontal mode**::
+
+ >>> con naz 0 eta 0 alpha 1
+
+**Z-axis mode (surface horizontal)**::
+
+ >>> con chi (-sigma) phi (-tau) alpha 1
+
+where sigma and tau are the offsets required in chi and phi to bring the surface
+normal parallel to eta. Alpha will determine mu directly leaving eta to orient
+the planes. Or::
+
+ >>> con naz 0 phi 0 alpha 1 # or any another sample angle
+
+**Z-axis mode (surface vertical)**::
+
+ >>> con naz 0 phi 0 alpha 1 # or any another sample angle
+
+Changing constrained values
+---------------------------
+
+Once constraints are chosen constrained values may be changed directly::
+
+ >>> con mu 10
+ gam : 0.0000
+ a_eq_b
+ mu : 10.0000
+
+or via the associated scannable::
+
+ >>> pos mu_con 10
+ mu_con: 10.00000
+
+Configuring limits and cuts
+---------------------------
+
+Diffcalc maintains its own limits on axes. These limits will be used when
+choosing solutions. If more than one detector solution is exists Diffcalc will
+ask you to reduce the the limits until there is only one. However if more than
+one solution for the sample settings is available it will choose one base on
+heuristics.
+
+Use the ``hardware`` command to see the current limits and cuts::
+
+ >>> hardware
+ mu (cut: -180.0)
+ delta (cut: -180.0)
+ gam (cut: -180.0)
+ eta (cut: -180.0)
+ chi (cut: -180.0)
+ phi (cut: 0.0)
+ Note: When auto sector/transforms are used,
+ cuts are applied before checking limits.
+
+To set the limits::
+
+ >>> setmin delta -1
+ >>> setmax delta 145
+
+To set a cut::
+
+ >>> setcut phi -180
+
+This causes requests to move phi to be between the configured -180 and +360
+degress above this. i.e. it might dive to -10 degrees rather than 350.
+
+
+Moving in hkl space
+-------------------
+
+Configure a mode, e.g. four-circle vertical::
+
+ >>> con gam 0 mu 0 a_eq_b
+ gam : 0.0000
+ a_eq_b
+ mu : 0.0000
+
+Simulate moving to a reflection::
+
+ >>> sim hkl [0 1 1]
+ sixc would move to:
+ mu : 0.0000
+ delta : 90.0000
+ gam : 0.0000
+ eta : 45.0000
+ chi : 45.0000
+ phi : 90.0000
+
+ alpha : 30.0000
+ beta : 30.0000
+ naz : 35.2644
+ psi : 90.0000
+ qaz : 90.0000
+ tau : 45.0000
+ theta : 45.0000
+
+Move to reflection::
+
+ >>> pos hkl [0 1 1]
+ hkl: h: 0.00000 k: 1.00000 l: 1.00000
+
+ >>> pos sixc
+ sixc: mu: 0.0000 delta: 90.0000 gam: 0.0000 eta: 45.0000 chi: 45.0000 phi: 90.0000
+
+Simulate moving to a location::
+
+ >>> pos sixc [0 60 0 30 90 0]
+ sixc: mu: 0.0000 delta: 60.0000 gam: 0.0000 eta: 30.0000 chi: 90.0000 phi: 0.0000
+
+Scanning in hkl space
+=====================
+
+All scans described below use the same generic scanning mechanism
+provided by the GDA system or by minigda. Here are some examples.
+
+Fixed hkl scans
+---------------
+
+In a 'fixed hkl scan' something (such as energy or Bin) is scanned,
+and at each step hkl is 'moved' to keep the sample and detector
+aligned. Also plonk the diffractometer scannable (sixc) on there with no
+destination to monitor what is actually happening and then
+throw on a detector (ct) with an exposure time if appropriate::
+
+ >>> #scan scannable_name start stop step [scannable_name [pos or time]]..
+
+ >>> scan en 9 11 .5 hkl [1 0 0] sixc ct 1
+
+ >>> scan en 9 11 .5 hklverbose [1 0 0] sixc ct 1
+
+ >>> scan betain 4 5 .2 hkl [1 0 0] sixc ct 1
+
+ >>> scan alpha_par 0 10 2 hkl [1 0 0] sixc ct 1
+
+Scanning hkl
+------------
+
+Hkl, or one component, may also be scanned directly::
+
+ >>> scan h .8 1.2 .1 hklverbose sixc ct 1
+
+At each step, this will read the current hkl position, modify the h
+component and then move to the resulting vector. There is a danger
+that with this method k and l may drift. To get around this the start,
+stop and step values may also be specified as vectors. So for example::
+
+ >>> scan hkl [1 0 0] [1 .3 0] [1 0.1 0] ct1
+
+is equivilant to::
+
+ >>> pos hkl [1 0 0]
+ >>> scan k 0 .3 .1 ct1
+
+but will not suffer from drifting. This method also allows scans along
+any direction in hkl space to be performed.
+
+Multidimension scans
+--------------------
+
+Two and three dimensional scans::
+
+ >>> scan en 9 11 .5 h .9 1.1 .2 hklverbose sixc ct 1
+ >>> scan h 1 3 1 k 1 3 1 l 1 3 1 hkl ct 1
+
+Commands
+========
+
+Orientation Commands
+--------------------
+
++-----------------------------+---------------------------------------------------+
+| **STATE** |
++-----------------------------+---------------------------------------------------+
+| **-- newub** {'name'} | start a new ub calculation name |
++-----------------------------+---------------------------------------------------+
+| **-- loadub** 'name' | num | load an existing ub calculation |
++-----------------------------+---------------------------------------------------+
+| **-- lastub** | load the last used ub calculation |
++-----------------------------+---------------------------------------------------+
+| **-- listub** | list the ub calculations available to load |
++-----------------------------+---------------------------------------------------+
+| **-- rmub** 'name'|num | remove existing ub calculation |
++-----------------------------+---------------------------------------------------+
+| **-- saveubas** 'name' | save the ub calculation with a new name |
++-----------------------------+---------------------------------------------------+
+| **LATTICE** |
++-----------------------------+---------------------------------------------------+
+| **-- setlat** | interactively enter lattice parameters (Angstroms |
+| | and Deg) |
++-----------------------------+---------------------------------------------------+
+| **-- setlat** name a | assumes cubic |
++-----------------------------+---------------------------------------------------+
+| **-- setlat** name a b | assumes tetragonal |
++-----------------------------+---------------------------------------------------+
+| **-- setlat** name a b c | assumes ortho |
++-----------------------------+---------------------------------------------------+
+| **-- setlat** name a b c | assumes mon/hex with gam not equal to 90 |
+| gamma | |
++-----------------------------+---------------------------------------------------+
+| **-- setlat** name a b c | arbitrary |
+| alpha beta gamma | |
++-----------------------------+---------------------------------------------------+
+| **-- c2th** [h k l] | calculate two-theta angle for reflection |
++-----------------------------+---------------------------------------------------+
+| **-- hklangle** [h1 k1 l1] | calculate angle between [h1 k1 l1] and [h2 k2 l2] |
+| [h2 k2 l2] | crystal planes |
++-----------------------------+---------------------------------------------------+
+| **REFERENCE (SURFACE)** |
++-----------------------------+---------------------------------------------------+
+| **-- setnphi** {[x y z]} | sets or displays n_phi reference |
++-----------------------------+---------------------------------------------------+
+| **-- setnhkl** {[h k l]} | sets or displays n_hkl reference |
++-----------------------------+---------------------------------------------------+
+| **REFLECTIONS** |
++-----------------------------+---------------------------------------------------+
+| **-- showref** | shows full reflection list |
++-----------------------------+---------------------------------------------------+
+| **-- addref** | add reflection interactively |
++-----------------------------+---------------------------------------------------+
+| **-- addref** [h k l] | add reflection with current position and energy |
+| {'tag'} | |
++-----------------------------+---------------------------------------------------+
+| **-- addref** [h k l] (p1, | add arbitrary reflection |
+| .., pN) energy {'tag'} | |
++-----------------------------+---------------------------------------------------+
+| **-- editref** num | interactively edit a reflection |
++-----------------------------+---------------------------------------------------+
+| **-- delref** num | deletes a reflection (numbered from 1) |
++-----------------------------+---------------------------------------------------+
+| **-- clearref** | deletes all the reflections |
++-----------------------------+---------------------------------------------------+
+| **-- swapref** | swaps first two reflections used for calculating |
+| | U matrix |
++-----------------------------+---------------------------------------------------+
+| **-- swapref** num1 num2 | swaps two reflections (numbered from 1) |
++-----------------------------+---------------------------------------------------+
+| **CRYSTAL ORIENTATIONS** |
++-----------------------------+---------------------------------------------------+
+| **-- showorient** | shows full list of crystal orientations |
++-----------------------------+---------------------------------------------------+
+| **-- addorient** | add crystal orientation interactively |
++-----------------------------+---------------------------------------------------+
+| **-- addorient** [h k l] | add crystal orientation in laboratory frame |
+| [x y z] {'tag'} | |
++-----------------------------+---------------------------------------------------+
+| **-- editorient** num | interactively edit a crystal orientation |
++-----------------------------+---------------------------------------------------+
+| **-- delorient** num | deletes a crystal orientation (numbered from 1) |
++-----------------------------+---------------------------------------------------+
+| **-- clearorient** | deletes all the crystal orientations |
++-----------------------------+---------------------------------------------------+
+| **-- swaporient** | swaps first two crystal orientations used for |
+| | calculating U matrix |
++-----------------------------+---------------------------------------------------+
+| **-- swaporient** num1 num2 | swaps two crystal orientations (numbered from 1) |
++-----------------------------+---------------------------------------------------+
+| **UB MATRIX** |
++-----------------------------+---------------------------------------------------+
+| **-- checkub** | show calculated and entered hkl values for |
+| | reflections |
++-----------------------------+---------------------------------------------------+
+| **-- setu** | manually set u matrix |
+| {[[..][..][..]]} | |
++-----------------------------+---------------------------------------------------+
+| **-- setub** | manually set ub matrix |
+| {[[..][..][..]]} | |
++-----------------------------+---------------------------------------------------+
+| **-- calcub** | (re)calculate u matrix from ref1 and ref2 |
++-----------------------------+---------------------------------------------------+
+| **-- trialub** | (re)calculate u matrix from ref1 only (check |
+| | carefully) |
++-----------------------------+---------------------------------------------------+
+| **-- refineub** {[h k l]} | refine unit cell dimensions and U matrix to match |
+| {pos} | diffractometer angles for a given hkl value |
++-----------------------------+---------------------------------------------------+
+| **-- addmiscut** angle | apply miscut to U matrix using a specified miscut |
+| {[x y z]} | angle in degrees and a rotation axis |
+| | (default: [0 1 0]) |
++-----------------------------+---------------------------------------------------+
+| **-- setmiscut** angle | manually set U matrix using a specified miscut |
+| {[x y z]} | angle in degrees and a rotation axis |
+| | (default: [0 1 0]) |
++-----------------------------+---------------------------------------------------+
+
+Motion commands
+---------------
+
++-----------------------------+---------------------------------------------------+
+| **CONSTRAINTS** |
++-----------------------------+---------------------------------------------------+
+| **-- con** | list available constraints and values |
++-----------------------------+---------------------------------------------------+
+| **-- con** {val} | constrains and optionally sets one constraint |
++-----------------------------+---------------------------------------------------+
+| **-- con** {val} | clears and then fully constrains |
+| {val} {val} | |
++-----------------------------+---------------------------------------------------+
+| **-- uncon** | remove constraint |
++-----------------------------+---------------------------------------------------+
+| **HKL** |
++-----------------------------+---------------------------------------------------+
+| **-- allhkl** [h k l] | print all hkl solutions ignoring limits |
++-----------------------------+---------------------------------------------------+
+| **HARDWARE** |
++-----------------------------+---------------------------------------------------+
+| **-- hardware** | show diffcalc limits and cuts |
++-----------------------------+---------------------------------------------------+
+| **-- setcut** {name {val}} | sets cut angle |
++-----------------------------+---------------------------------------------------+
+| **-- setmin** {axis {val}} | set lower limits used by auto sector code (None |
+| | to clear) |
++-----------------------------+---------------------------------------------------+
+| **-- setmax** {name {val}} | sets upper limits used by auto sector code (None |
+| | to clear) |
++-----------------------------+---------------------------------------------------+
+| **MOTION** |
++-----------------------------+---------------------------------------------------+
+| **-- sim** hkl scn | simulates moving scannable (not all) |
++-----------------------------+---------------------------------------------------+
+| **-- sixc** | show Eularian position |
++-----------------------------+---------------------------------------------------+
+| **-- pos** sixc [mu, delta, | move to Eularian position(None holds an axis |
+| gam, eta, chi, phi] | still) |
++-----------------------------+---------------------------------------------------+
+| **-- sim** sixc [mu, delta, | simulate move to Eulerian positionsixc |
+| gam, eta, chi, phi] | |
++-----------------------------+---------------------------------------------------+
+| **-- hkl** | show hkl position |
++-----------------------------+---------------------------------------------------+
+| **-- pos** hkl [h k l] | move to hkl position |
++-----------------------------+---------------------------------------------------+
+| **-- pos** {h | k | l} val | move h, k or l to val |
++-----------------------------+---------------------------------------------------+
+| **-- sim** hkl [h k l] | simulate move to hkl position |
++-----------------------------+---------------------------------------------------+
+
+Good luck --- RobW
+
+References
+==========
+
+.. [You1999] H. You. *Angle calculations for a '4S+2D' six-circle diffractometer.*
+ J. Appl. Cryst. (1999). **32**, 614-623. `(pdf link)
+ `__.
+.. [Busing1967] W. R. Busing and H. A. Levy. *Angle calculations for 3- and 4-circle X-ray
+ and neutron diffractometers.* Acta Cryst. (1967). **22**, 457-464. `(pdf link)
+ `__.
diff --git a/script/__Lib/diffcalc-2.1/doc/source/youmanual_images/4s_2d_diffractometer.png b/script/__Lib/diffcalc-2.1/doc/source/youmanual_images/4s_2d_diffractometer.png
new file mode 100755
index 0000000..5216255
Binary files /dev/null and b/script/__Lib/diffcalc-2.1/doc/source/youmanual_images/4s_2d_diffractometer.png differ
diff --git a/script/__Lib/diffcalc-2.1/doc/source/youmanual_template.rst b/script/__Lib/diffcalc-2.1/doc/source/youmanual_template.rst
new file mode 100755
index 0000000..1b32fff
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/source/youmanual_template.rst
@@ -0,0 +1,565 @@
+################################
+Diffcalc User Guide (You Engine)
+################################
+
+.. rubric:: Diffcalc: A diffraction condition calculator for diffractometer control
+
+:Author: Rob Walton
+:Contact: rob.walton (at) diamond (dot) ac (dot) uk
+:Website: https://github.com/DiamondLightSource/diffcalc
+
+.. toctree::
+ :maxdepth: 2
+ :numbered:
+
+See also the `quickstart guide at github `_
+
+Introduction
+============
+
+This manual assumes that you are running Diffcalc within OpenGDA or have started
+it using IPython. It assumes that Diffcalc has been configured for the six
+circle diffractometer pictured here:
+
+.. figure:: youmanual_images/4s_2d_diffractometer.png
+ :scale: 100
+ :align: center
+
+ 4s + 2d six-circle diffractometer, from H.You (1999)
+
+Your Diffcalc configuration may have been customised for the geometry of your
+diffractometer and possibly the types of experiment you perform. For example, a
+five-circle diffractometer might be missing the nu circle above.
+
+The laboratory frame is shown above. With all settings at zero as shown the
+crystal cartesian frame aligns with the laboratory frame. Therefor a cubic
+crystal mounted squarely in a way that the U matrix (defined below) is unitary
+will have h||a||x, k||b||y & l||c||z, crystal and reciprocal-lattice coordinate
+frames are defined with respect to the beam and to gravity to be (for a cubic
+crystal):
+
+Overview
+========
+
+The following assumes that the diffractometer has been properly leveled, aligned
+with the beam and zeroed. See the `SPEC fourc manual
+`__.
+
+Before moving in hkl space you must calculate a UB matrix by specifying the
+crystal's lattice parameters (which define the B matrix) and finding two
+reflections (from which the U matrix defining any mismount can be inferred);
+and, optionally for surface-diffraction experiments, determine how the surface
+of the crystal is oriented with respect to the phi axis.
+
+Once a UB matrix has been calculated, the diffractometer may be driven in hkl
+coordinates. A valid diffractometer setting maps easily into a single hkl value.
+However for a diffractometer with more than three circles there are excess
+degrees of freedom when calculating a diffractometer setting from an hkl value.
+Diffcalc provides modes for using up the excess degrees of freedom.
+
+Diffcalc does not perform scans directly. Instead, Scannables that use diffcalc
+to map between reciprocal lattice space and real diffractometer settings are
+scanned using the Gda's (or minigda's) generic scan mechanism.
+
+
+Theory
+------
+
+Thanks to Elias Vlieg for sharing his dos based ``DIF`` software that Diffcalc
+has borrowed heavily from. The version of Diffcalc described here is based on papers by
+pHH. You. [You1999]_ and Busing & Levy [Busing1967]_. (See also the THANKS.txt file.)
+
+Getting Help
+============
+
+There are few commands to remember. If a command is called without
+arguments in some cases Diffcalc will prompt for arguments and provide sensible
+defaults which can be chosen by pressing enter.
+
+
+**Orientation**. The ``helpub`` command lists all commands related with crystal
+orientation and the reference vector (often used with surfaces). See the
+`Orientation Commands`_ section at the end of this manual::
+
+ >>> help ub
+ ...
+
+
+**HKL movement**. The ``help hkl`` list all commands related to moving in reciprocal-lattice
+space. See the `Motion Commands`_ section at the end of this manual::
+
+ >>> help hkl
+ ...
+
+
+Call help on any command. e.g.::
+
+ ==> help loadub
+
+Diffcalc's Scannables
+=====================
+
+To list and show the current positions of your beamline's scannables
+use ``pos`` with no arguments::
+
+ >>> pos
+
+Results in:
+
+**Energy and wavelength scannables**::
+
+ energy 12.3984
+ wl: 1.0000
+
+**Diffractometer scannables**, as a group and in component axes (in
+the real GDA these have limits)::
+
+ sixc: mu: 0.0000 delta: 0.0000 gamma: 0.0000 omega: 0.0000 chi: 0.0000 phi: 0.0000
+ mu: 0.0000
+ chi: 0.0000
+ delta: 0.0000
+ gamma: 0.0000
+ omega: 0.0000
+ phi: 0.0000
+
+**Dummy counter**, which in this example simply counts at 1hit/s::
+
+ ct: 0.0000
+
+**Hkl scannable**, as a group and in component::
+
+ hkl: Error: No UB matrix
+ h: Error: No UB matrix
+ k: Error: No UB matrix
+ l: Error: No UB matrix
+
+**Parameter scannables**, used in some modes, these provide a
+scannable alternative to the `Motion`_ section. Some constrain of
+these constrain virtual angles::
+
+ alpha: ---
+ beta: ---
+ naz: ---
+ psi: ---
+ qaz: ---
+
+and some constrain physical angles::
+
+ phi_con: ---
+ chi_con: ---
+ delta_con:---
+ eta_con: ---
+ gam_con: ---
+ mu_con: ---
+
+
+Crystal orientation
+===================
+
+Before moving in hkl space you must calculate a UB matrix by specifying the
+crystal's lattice parameters (which define the B matrix) and finding two
+reflections (from which the U matrix can be inferred); and, optionally for
+surface-diffraction experiments, determine how the surface of the crystal is
+oriented with respect to the phi axis.
+
+Start a new UB calculation
+--------------------------
+
+A *UB calculation* contains the description of the crystal-under-test,
+any saved reflections, reference angle direction, and a B & UB
+matrix pair if they have been calculated or manually specified.
+Starting a new UB calculation will clear all of these.
+
+Before starting a UB-calculation, the ``ub`` command used to summarise
+the state of the current UB-calculation, will reflect that no
+UB-calculation has been started::
+
+ ==> ub
+
+A new UB-calculation calculation may be started and lattice specified
+explicitly::
+
+ ==> newub 'example'
+ ==> setlat '1Acube' 1 1 1 90 90 90
+
+or interactively::
+
+ >>> newub
+ calculation name: example
+ crystal name: 1Acube
+ a [1]: 1
+ b [1]: 1
+ c [1]: 1
+ alpha [90]: 90
+ beta [90]: 90
+ gamma [90]: 90
+
+where a,b and c are the lengths of the three unit cell basis vectors
+in Angstroms, and alpha, beta and gamma are angles in Degrees.
+
+The ``ub`` command will show the state of the current UB-calculation
+(and the current energy for reference)::
+
+ ==> ub
+
+Load a UB calculation
+---------------------
+
+To load the last used UB-calculation::
+
+ >>> lastub
+ Loading ub calculation: 'mono-Si'
+
+To load a previous UB-calculation::
+
+ >>> listub
+ UB calculations in: /Users/walton/.diffcalc/i16
+
+ 0) mono-Si 15 Feb 2017 (22:32)
+ 1) i16-32 13 Feb 2017 (18:32)
+
+ >>> loadub 0
+
+Generate a U matrix from two reflections
+----------------------------------------
+
+The normal way to calculate a U matrix is to find the position of **two**
+reflections with known hkl values. Diffcalc allows many reflections to be
+recorded but currently only uses the first two when calculating a UB matrix.
+
+Find U matrix from two reflections::
+
+ ==> pos wl 1
+ ==> c2th [0 0 1]
+ 59.99999999999999
+
+ ==> pos sixc [0 60 0 30 90 0]
+ ==> addref [0 0 1]
+
+ ==> pos sixc [0 90 0 45 45 90]
+ ==> addref [0 1 1]
+
+Check that it looks good::
+
+ ==> checkub
+
+Generate a U matrix from one reflection
+---------------------------------------
+
+To estimate based on first reflection only::
+
+ ==> trialub
+
+Manually specify U matrix
+-------------------------
+
+Set U matrix manually (pretending sample is squarely mounted)::
+
+ ==> setu [[1 0 0] [0 1 0] [0 0 1]]
+
+Edit reflection list
+--------------------
+
+Use ``showref`` to show the reflection list::
+
+ ==> showref
+
+Use ``swapref`` to swap reflections::
+
+ ==> swapref 1 2
+ Recalculating UB matrix.
+
+Use ``delref`` to delete a reflection::
+
+ >>> delref 1
+
+Calculate a UB matrix
+---------------------
+
+Unless a U or UB matrix has been manually specified, a new UB matrix will be
+calculated after the second reflection has been found, or whenever one of the
+first two reflections is changed.
+
+Use the command ``calcub`` to force the UB matrix to be calculated from the
+first two reflections.
+
+If you have misidentified a reflection used for the orientation the
+resulting UB matrix will be incorrect. Always use the ``checkub``
+command to check that the computed values agree with the estimated values::
+
+ ==> checkub
+
+Set the reference vector
+-------------------------
+
+When performing surface experiments the reference vector should be set normal
+to the surface. It can also be used to define other directions within the crystal
+with which we want to orient the incident or diffracted beam.
+
+By default the reference vector is set parallel to the phi axis. That is,
+along the z-axis of the phi coordinate frame.
+
+The `ub` command shows the current reference vector, along with any inferred
+miscut, at the top its report (or it can be shown by calling ``setnphi`` or
+``setnhkl'`` with no args)::
+
+ >>> ub
+ ...
+ n_phi: 0.00000 0.00000 1.00000 <- set
+ n_hkl: -0.00000 0.00000 1.00000
+ miscut: None
+ ...
+
+The ``<- set`` label here indicates that the reference vector is set in the phi
+coordinate frame. In this case, therefor, its direction in the crystal's
+reciprocal lattice space is inferred from the UB matrix.
+
+To set the reference vector in the phi coordinate frame use::
+
+ >>> setnphi [0 0 1]
+ ...
+
+This is useful if the surface normal has be found with a laser or by x-ray
+occlusion. This vector must currently be manually calculated from the sample
+angle settings required to level the surface (sigma and tau commands on the
+way).
+
+To set the reference vector in the crystal's reciprocal lattice space use (this
+is a quick way to determine the surface orientation if the surface is known to
+be cleaved cleanly along a known axis)::
+
+ >>> setnhkl [0 0 1] ...
+
+Motion
+======
+
+Once a UB matrix has been calculated, the diffractometer may be driven
+in hkl coordinates. A given diffractometer setting maps easily into a
+single hkl value. However for a diffractometer with more than three circles
+there are excess degrees of freedom when calculating a diffractometer
+setting from an hkl value. Diffcalc provides many for using up
+the excess degrees of freedom.
+
+By default Diffcalc selects no mode.
+
+Constraining solutions for moving in hkl space
+----------------------------------------------
+
+To get help and see current constraints::
+
+ >>> help con
+ ...
+
+ ==> con
+
+Three constraints can be given: zero or one from the DET and REF columns and the
+remainder from the SAMP column. Not all combinations are currently available.
+Use ``help con`` to see a summary if you run into troubles.
+
+To configure four-circle vertical scattering::
+
+ ==> con gam 0 mu 0 a_eq_b
+
+In the following the *scattering plane* is defined as the plane including the
+scattering vector, or momentum transfer vector, and the incident beam.
+
+**DETECTOR COLUMN:**
+
+- **delta** - physical delta setting (vertical detector motion) *del=0 is equivalent to qaz=0*
+- **gam** - physical gamma setting (horizontal detector motion) *gam=0 is equivalent to qaz=90*
+- **qaz** - azimuthal rotation of scattering vector (about the beam, from horizontal)
+- **naz** - azimuthal rotation of reference vector (about the beam, from horizontal)
+
+**REFERENCE COLUMN:**
+
+- **alpha** - incident angle to surface (if reference is normal to surface)
+- **beta** - exit angle from surface (if reference is normal to surface)
+- **psi** - azimuthal rotation about scattering vector of reference vector (from scattering plane)
+- **a_eq_b** - bisecting mode with alpha=beta. *Equivalent to psi=90*
+
+**SAMPLE COLUMN:**
+
+- **mu, eta, chi & phi** - physical settings
+- **mu_is_gam** - force mu to follow gamma (results in a 5-circle geometry)
+
+Diffcalc will report two other (un-constrainable) virtual angles:
+
+- **theta** - half of 2theta, the angle through the diffracted beam bends
+- **tau** - longitude of reference vector from scattering vector (in scattering plane)
+
+Example constraint modes
+------------------------
+
+There is sometimes more than one way to get the same effect.
+
+**Vertical four-circle mode**::
+
+ >>> con gam 0 mu 0 a_eq_b # or equivalently:
+ >>> con qaz 90 mu 0 a_eq_b
+
+ >>> con alpha 1 # replaces a_eq_b
+
+**Horizontal four-circle mode**::
+
+ >>> con del 0 eta 0 alpha 1 # or equivalently:
+ >>> con qaz 0 mu 0 alpha 1
+
+**Surface vertical mode**::
+
+ >>> con naz 90 mu 0 alpha 1
+
+**Surface horizontal mode**::
+
+ >>> con naz 0 eta 0 alpha 1
+
+**Z-axis mode (surface horizontal)**::
+
+ >>> con chi (-sigma) phi (-tau) alpha 1
+
+where sigma and tau are the offsets required in chi and phi to bring the surface
+normal parallel to eta. Alpha will determine mu directly leaving eta to orient
+the planes. Or::
+
+ >>> con naz 0 phi 0 alpha 1 # or any another sample angle
+
+**Z-axis mode (surface vertical)**::
+
+ >>> con naz 0 phi 0 alpha 1 # or any another sample angle
+
+Changing constrained values
+---------------------------
+
+Once constraints are chosen constrained values may be changed directly::
+
+ ==> con mu 10
+
+or via the associated scannable::
+
+ ==> pos mu_con 10
+
+Configuring limits and cuts
+---------------------------
+
+Diffcalc maintains its own limits on axes. These limits will be used when
+choosing solutions. If more than one detector solution is exists Diffcalc will
+ask you to reduce the the limits until there is only one. However if more than
+one solution for the sample settings is available it will choose one base on
+heuristics.
+
+Use the ``hardware`` command to see the current limits and cuts::
+
+ ==> hardware
+
+To set the limits::
+
+ ==> setmin delta -1
+ ==> setmax delta 145
+
+To set a cut::
+
+ ==> setcut phi -180
+
+This causes requests to move phi to be between the configured -180 and +360
+degress above this. i.e. it might dive to -10 degrees rather than 350.
+
+
+Moving in hkl space
+-------------------
+
+Configure a mode, e.g. four-circle vertical::
+
+ ==> con gam 0 mu 0 a_eq_b
+
+Simulate moving to a reflection::
+
+ ==> sim hkl [0 1 1]
+
+Move to reflection::
+
+ ==> pos hkl [0 1 1]
+
+ ==> pos sixc
+
+Simulate moving to a location::
+
+ ==> pos sixc [0 60 0 30 90 0]
+
+Scanning in hkl space
+=====================
+
+All scans described below use the same generic scanning mechanism
+provided by the GDA system or by minigda. Here are some examples.
+
+Fixed hkl scans
+---------------
+
+In a 'fixed hkl scan' something (such as energy or Bin) is scanned,
+and at each step hkl is 'moved' to keep the sample and detector
+aligned. Also plonk the diffractometer scannable (sixc) on there with no
+destination to monitor what is actually happening and then
+throw on a detector (ct) with an exposure time if appropriate::
+
+ >>> #scan scannable_name start stop step [scannable_name [pos or time]]..
+
+ >>> scan en 9 11 .5 hkl [1 0 0] sixc ct 1
+
+ >>> scan en 9 11 .5 hklverbose [1 0 0] sixc ct 1
+
+ >>> scan betain 4 5 .2 hkl [1 0 0] sixc ct 1
+
+ >>> scan alpha_par 0 10 2 hkl [1 0 0] sixc ct 1
+
+Scanning hkl
+------------
+
+Hkl, or one component, may also be scanned directly::
+
+ >>> scan h .8 1.2 .1 hklverbose sixc ct 1
+
+At each step, this will read the current hkl position, modify the h
+component and then move to the resulting vector. There is a danger
+that with this method k and l may drift. To get around this the start,
+stop and step values may also be specified as vectors. So for example::
+
+ >>> scan hkl [1 0 0] [1 .3 0] [1 0.1 0] ct1
+
+is equivilant to::
+
+ >>> pos hkl [1 0 0]
+ >>> scan k 0 .3 .1 ct1
+
+but will not suffer from drifting. This method also allows scans along
+any direction in hkl space to be performed.
+
+Multidimension scans
+--------------------
+
+Two and three dimensional scans::
+
+ >>> scan en 9 11 .5 h .9 1.1 .2 hklverbose sixc ct 1
+ >>> scan h 1 3 1 k 1 3 1 l 1 3 1 hkl ct 1
+
+Commands
+========
+
+Orientation Commands
+--------------------
+
+==> UB_HELP_TABLE
+
+Motion commands
+---------------
+
+==> HKL_HELP_TABLE
+
+Good luck --- RobW
+
+References
+==========
+
+.. [You1999] H. You. *Angle calculations for a '4S+2D' six-circle diffractometer.*
+ J. Appl. Cryst. (1999). **32**, 614-623. `(pdf link)
+ `__.
+.. [Busing1967] W. R. Busing and H. A. Levy. *Angle calculations for 3- and 4-circle X-ray
+ and neutron diffractometers.* Acta Cryst. (1967). **22**, 457-464. `(pdf link)
+ `__.
diff --git a/script/__Lib/diffcalc-2.1/doc/tmp/constraints.txt b/script/__Lib/diffcalc-2.1/doc/tmp/constraints.txt
new file mode 100755
index 0000000..5c64bf2
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/tmp/constraints.txt
@@ -0,0 +1,47 @@
+Take a six circle diffractometer with angles:
+
+delta, nu, mu, eta, chi, phi
+
+and virtual angles:
+
+qaz, naz, psi, alpha, beta.
+
+Many combinations of these can be constrained:
+
+
+constrain
+>>> con nu
+>>> con psi
+>>> con mu
+>>> con nu mu a_eq_b
+
+real angles are always constrained to there last set position. If they are too far from here
+an error will result.
+
+>>> pos nu 0 (moves, parameter tracks last set value (actually checks it when hkl moved))
+>>> pos psi 90 (does not move, sets parameter in diffcalc)
+>>> pos mu 0 (moves)
+>>> pos hkl [1 0 0] (moves based on last set nu, psi, mu)
+>>> pos betain --> Exception, not constrained
+
+moving a virtual angle simply sets it to efftec the next hkl move
+
+Here we need onlu con, uncon and pos.
+
+......
+
+However it would be nice to be able to pos betain for example and have it change
+betain while staying on the same reflection. This might be enabled by locking the
+the current hkl position. It would be trick to make this work with the physical angles though.
+
+>>> lock hkl
+>>> pos psi 90 (moves immediately)
+>>> scan psi 0 90 1 (scans, moving immediately)
+>>> pos mu 0 (does not move immediately) (could work by listening to target positions, and triggering move when complete)
+>>> pos c(mu) moves
+>>> pos mu_c moves
+
+Only one thing would be varyable at a time unless they are put in a CoordinatedMotionGroup,
+which would be hard for the real motors)
+
+
diff --git a/script/__Lib/diffcalc-2.1/doc/tmp/extensions_to_yous_paper.wxm b/script/__Lib/diffcalc-2.1/doc/tmp/extensions_to_yous_paper.wxm
new file mode 100755
index 0000000..1e866bf
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/tmp/extensions_to_yous_paper.wxm
@@ -0,0 +1,525 @@
+/* [wxMaxima batch file version 1] [ DO NOT EDIT BY HAND! ]*/
+/* [ Created with wxMaxima version 11.08.0 ] */
+
+/* [wxMaxima: input start ] */
+Rx:matrix([1,0,0],[0, cos(theta), -sin(theta)],[0, sin(theta), cos(theta)])$
+Ry:matrix([cos(theta), 0, sin(theta)], [0, 1, 0], [-sin(theta), 0, cos(theta)])$
+Rz:matrix([cos(theta), -sin(theta), 0], [sin(theta), cos(theta), 0], [0, 0, 1])$
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+Equation 5:
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+PHI:subst(-phi, theta, Rz);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+CHI:subst(chi, theta, Ry);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+ETA:subst(-eta, theta, Rz);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+MU:subst(mu, theta, Rx);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+Generic V:
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+V:matrix([v11, v12, v13], [v21, v22, v23], [v31, v32, v33]);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: section start ]
+When one sample-orienting angle is given
+ [wxMaxima: section end ] */
+
+/* [wxMaxima: subsect start ]
+For mu fixed
+ [wxMaxima: subsect end ] */
+
+/* [wxMaxima: input start ] */
+eq34:ETA.CHI.PHI;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq34_chi0:subst(0, chi, eq34);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+*phi* with chi <> 0
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+trigreduce(trigsimp((eq34[3,2]=V[3,2])/(eq34[3,1]=V[3,1])));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+show that with phi with v31 == 0 and v12 <> 0, no special case is needed when using atan2
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+solve(cos(phi)=0, phi);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+atan2(1234,0);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+show that with phi with v32 == 0 and v31 <> 0, no special case is needed when using atan2
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+solve(sin(phi)=0, phi);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+atan2(0,1234);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+*eta* with chi <> 0
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+trigreduce(trigsimp((-eq34[2,3]=V[2,3]) / (eq34[1, 3]=V[1,3])));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+eta with v23 == 0 and v13 == 0 (phi||eta degeneracy because chi == 0)
+Then see equation for phi+eta above
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: comment start ]
+*chi*
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+trigsimp(sqrt((eq34[3,1]=V[3,1])^2+(eq34[3,2]=V[3,2])^2)/(eq34[3,3]=V[3,3]));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+*phi + eta* with chi == 0
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+trigreduce(trigrat((eq34_chi0[1,2]=V[1,2])/(eq1:eq34_chi0[1,1]=V[1,1])));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: subsect start ]
+For phi fixed
+ [wxMaxima: subsect end ] */
+
+/* [wxMaxima: input start ] */
+eq36:MU.ETA.CHI;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq36_eta90:subst(%pi/2, eta, eq36);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+*eta*
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+trigsimp((eq36[1,2]=V[1,2])/sqrt((eq36[2,2]=V[2,2])^2+(eq36[3,2]=V[3,2])^2));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+*mu* with eta <> +-90
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+trigreduce(trigsimp((eq36[3,2]=V[3,2])/(eq36[2,2]=V[2,2])));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+*chi* with eta <> +-90
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+trigreduce(trigsimp((eq36[1,3]=V[1,3])/(eq36[1,1]=V[1,1])));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+*chi + mu* with eta == +-90
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+trigreduce(trigrat((eq36_eta90[2,3]=V[2,3])/(eq36_eta90[2,1]=V[2,1])));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: subsect start ]
+For eta or chi fixed
+ [wxMaxima: subsect end ] */
+
+/* [wxMaxima: input start ] */
+eq38:MU.ETA.CHI.PHI;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+*chi* (eq. 39) given eta, and eta <> +-90 (with eta == +-90 chi||mu)
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+(eq38[1,3]=V[1,3])/cos(eta);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: comment start ]
+*eta* given chi, and chi <> 0 (with chi == 0 phi||eta)
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+(eq38[1,3]=V[1,3])/sin(chi);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+*mu* given that chi and eta have been given or calclulated respecitively
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+top:(eq38[3,3]=V[3,3])*sin(eta)*sin(chi) + (eq38[2,3]=V[2,3])*cos(chi);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+bot:-(eq38[3,3]=V[3,3])*cos(chi)+(eq38[2,3]=V[2,3])*sin(eta)*sin(chi);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+factor(top);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+factor(bot);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+trigsimp(top/bot);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+*phi - mu* when phi || mu because chi == +-90, eta == 0 or eta == 180
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+phi_mu_parallel:subst(0, eta, subst(%pi/2, chi, eq38));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+trigreduce(trigrat((phi_mu_parallel[2,1]=V[2,1])/(phi_mu_parallel[2,2]=V[2,2])));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: section start ]
+Two sample angles given
+ [wxMaxima: section end ] */
+
+/* [wxMaxima: input start ] */
+THETA:subst(-theta, theta, Rz);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+PSI:subst(psi, theta, Rx);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+F:subst(xi, theta, Ry);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: section start ]
+psi, mu, eta given
+ [wxMaxima: section end ] */
+
+/* [wxMaxima: input start ] */
+eq49:transpose(PHI).transpose(CHI).transpose(ETA).transpose(MU).F;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+*chi*
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: comment start ]
+using the linear combination identity
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+comb_idenity:sqrt(A^2+B^2)*sin(x+omega);
+omega:atan(B/A);
+A*sin(x)+B*cos(x)=comb_idenity;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+a:-sin(eta)*cos(mu)$
+b:-sin(mu)$
+V32=a*sin(chi)+b*cos(chi);
+V32=subst(b, B, subst(a, A, comb_idenity));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+asin(%/(sqrt(sin(mu)^2+sin(eta)^2*cos(mu)^2))), triginverses=all;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+*xi and phi*
+
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+top:factor(eq49[3,1]*cos(xi)+eq49[3,3]*sin(xi));
+bot:factor(eq49[3,1]*sin(xi)-eq49[3,3]*cos(xi));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+factor(top/bot);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+When mu=-90 and eta = 0, used to for a surface normal vertical mode
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+eq49_vert:subst(-%pi/2, mu, subst(0, eta, eq49));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+(eq49_vert[2,2]=V[2,2]) / (eq49_vert[1,2]=V[1,2]);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: subsect start ]
+mu, phi, qaz (for three circle on i10 and i06, not in paper )
+ [wxMaxima: subsect end ] */
+
+/* [wxMaxima: input start ] */
+thisV:transpose(MU).F.THETA;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+Nphi:matrix([n11, n12, n13], [n21, n22, n23], [n31, n32, n33]);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq:ETA.CHI.PHI.Nphi.transpose(PSI);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+*chi*
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: comment start ]
+using the linear combination identity
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+comb_idenity:sqrt(A^2+B^2)*sin(x+omega);
+omega:atan(B/A);
+A*sin(x)+B*cos(x)=comb_idenity;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+on V31:
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+a:-1*(n21*sin(phi)+n11*cos(phi))$
+b:n31$
+V31=a*sin(chi)+b*cos(chi);
+V31=subst(b, B, subst(a, A, comb_idenity));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+asin(%/sqrt((-n21*sin(phi)-n11*cos(phi))^2+n31^2)), triginverses=all;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+*xi and phi*
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+top:factor(eq49[3,1]*cos(xi)+eq49[3,3]*sin(xi));
+bot:factor(eq49[3,1]*sin(xi)-eq49[3,3]*cos(xi));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+factor(top/bot);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+solve();
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: section start ]
+Fixed mu, chi, psi (probably with mu=chi=0) (not in paper)
+ [wxMaxima: section end ] */
+
+/* [wxMaxima: input start ] */
+Vcalulated:Nphi.transpose(PSI).transpose(THETA);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+thisV:transpose(PHI).transpose(CHI).transpose(ETA).transpose(MU).F;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+v:thisVvert:subst(0, chi, subst(0, mu, thisV));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+acos(thisVvert[3,3]=V[3,3]), triginverses=all;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+top:(v[1,2]=V[1,2])*cos(phi) + (v[2,2]=V[2,2])*sin(phi);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+bot:(v[1,2]=V[1,2])*sin(phi) - (v[2,2]=V[2,2])*cos(phi);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+factor(top);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+factor(bot);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+trigsimp(top/bot);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: title start ]
+5.5 subset with eta=chi=0 & phi set
+ [wxMaxima: title end ] */
+
+/* [wxMaxima: input start ] */
+matrix([cos(theta)*sin(qaz)], [-sin(theta)], [cos(theta)*cos(qaz)]);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+h_phi:matrix([h1], [h2], [h3]);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+Z:MU.ETA.CHI.PHI;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq68yterm:-sin(theta) = ((Z.h_phi))[2][1];
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+expands(%);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq68_chi_eta_0:subst(0, chi, subst(0, eta, Z.h_phi));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq68_chi_eta_0_yterm:-sin(theta)=eq68_chi_eta_0[2];
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+subst(0, chi, subst(0, eta, eq68yterm));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+a;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+ratsubst(a, -h3, eq68_chi_eta_0_yterm);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+a:-h3;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+b:-h1*sin(phi) +h2*cos(phi);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+c:-sin(theta);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+ratsubst(cc, c, ratsubst(bb, b, ratsubst(aa, a, eq68_chi_eta_0_yterm)));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+arcsin(c/sqrt(a^2+b^2)) - arctan(b, a);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq68_chi_eta_0[1] /eq68_chi_eta_0[2];
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+without chi=eta=0
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+eq68yterm:-sin(theta) = ((Z.h_phi))[2][1];
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+expand(eq68yterm);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+solve for mu unknown
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+c:-sin(theta);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+a:sin(chi)*h2*sin(phi) + sin(chi)*h1*cos(phi) - cos(chi)*h3;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+b: -cos(chi)*sin(eta)*h2*sin(phi) -cos(eta)*h1*sin(phi) +cos(eta)*h2*cos(phi) -cos(chi)*sin(eta)*h1*cos(phi) - sin(chi)*sin(eta)*h3;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+expand(subst(a, aa, subst(b, bb, -sin(theta)=aa*sin(mu) + bb*cos(mu))));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+Check a and b expand to produce original (Okay)
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+expand(eq68yterm);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: comment start ]
+Check against special case above (Okay)
+ [wxMaxima: comment end ] */
+
+/* [wxMaxima: input start ] */
+subst(0, chi, subst(0, eta, a));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+subst(0, chi, subst(0, eta, b));
+/* [wxMaxima: input end ] */
+
+/* Maxima can't load/batch files which end with a comment! */
+"Created with wxMaxima"$
diff --git a/script/__Lib/diffcalc-2.1/doc/tmp/i16_non_diffcalc_manual.txt b/script/__Lib/diffcalc-2.1/doc/tmp/i16_non_diffcalc_manual.txt
new file mode 100755
index 0000000..16aa9d6
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/doc/tmp/i16_non_diffcalc_manual.txt
@@ -0,0 +1,71 @@
+Creating a UB Matrix
+There are three euler modes:
+
+=bisecting: eta and delta are fixed, phi and chi are not fixed.
+=fixed phi: phi and 2theta are fixed, eta and chi are not fixed.
+=fixed psi: Not used for creating a UB matrix .
+First creat a reflection file to store the reflections data using:
+
+reffile('name')
+
+Set the crystal lattice:
+
+latt([a]) assumes cubic
+latt([a,b]) assumes tetragonal
+latt([a,b,c]) assumes orthorhombic
+latt([a,b,c,gam]) assumes monclinic/hexagonal gam different from 90.
+latt([a,b,c,alp,bet,gam])
+
+Use:
+
+c2th([h k l])
+
+to calculate the 2 theta position for the hkl for example:
+
+c2th([0 0 2])
+
+Then use the following to move the diffractometer to the correct positions:
+
+pos delta c2th([0 0 2]) eta c2th([0 0 2])/2
+
+You can now scan eta, chi, and delta to optomise the peak intensity using the following scans for example:
+
+scancn eta 0.01 40 w 0.2 t 1
+
+scancn chi 0.01 40 w 0.2 t 1
+
+scancn delta 0.01 40 w 0.2 t 1
+
+A few iterations may be required.
+
+Now that you have the first reflection it can be stored using the following command:
+
+saveref('reflable',[h k l])
+
+saveref('Ti1',[0 0 2]) for the above example.
+
+Then create a dummy UB matrix using a dummy non-parallel hkl plane eg:
+
+ubm('Ti1',[2 0 0])
+
+Useing the following command to calculate the direction of the second reflection:
+
+hkl_calc([0 2 2])
+
+If the calculated positions can be safely achieved then type:
+
+pos hkl [0 2 2]
+
+Find the second reflection by scanning phi, once the reflection is found save the second reflection using:
+
+saveref('Ti2',[0 2 2]) for example.
+
+Now create a real UB-Matrix using:
+
+ubm('Ti1','Ti2')
+
+You can now drive in recriprocal space.
+
+To change the lattice parameters according the found Bragg peaks
+
+latt([latt()[0]*2/l()]) (cubic only)
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/install-jython-environment.sh b/script/__Lib/diffcalc-2.1/install-jython-environment.sh
new file mode 100755
index 0000000..4ac8ef1
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/install-jython-environment.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env sh
+
+JYTHON_URL='http://search.maven.org/remotecontent?filepath=org/python/jython-installer/2.7.1/jython-installer-2.7.1.jar'
+JAMA_JAR_URL='http://math.nist.gov/javanumerics/jama/Jama-1.0.3.jar'
+
+# Install Jama jar
+mkdir -p lib
+wget -P $HOME/lib $JAMA_JAR_URL
+export CLASSPATH=$HOME/lib/Jama-1.0.3.jar:$CLASSPATH
+
+# Install Jython
+wget $JYTHON_URL -O jython_installer.jar
+java -jar jython_installer.jar -s -d $HOME/jython
+
+# Install nose for Jython
+# TODO: move to a setup.py
+$HOME/jython/bin/pip install nose
+$HOME/jython/bin/pip install pytest==3.2.3
+$HOME/jython/bin/pip install pytest-xdist
diff --git a/script/__Lib/diffcalc-2.1/integration_checks.py b/script/__Lib/diffcalc-2.1/integration_checks.py
new file mode 100755
index 0000000..d3fdacc
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/integration_checks.py
@@ -0,0 +1,72 @@
+'''
+Created on 9 Mar 2017
+
+@author: zrb13439
+
+'''
+
+'''
+Integration tests for startup scripts (named so that nosetests will *not* pick
+up by default.
+
+This is because these must be run with:
+
+ $ nosetests --with-process-isolation integration_checks.py
+
+This is not a standard nose option. Install it with:
+
+ $ pip install nosepipe
+
+nosepipe is described at https://github.com/dmccombs/nosepipe/
+'''
+
+from nose import SkipTest
+
+
+def test_sixcircle_startup():
+ import startup.sixcircle
+ startup.sixcircle.ct.pause = False
+ startup.sixcircle.demo.all()
+
+
+def test_fivecircle_startup():
+ import startup.fivecircle
+ startup.fivecircle.ct.pause = False
+ startup.fivecircle.demo.all()
+
+
+def test_fourcircle_startup():
+ import startup.fourcircle
+ startup.fourcircle.ct.pause = False
+ startup.fourcircle.demo.all()
+
+
+def test_i13_startup():
+ raise SkipTest('Still need to work out to use i13s very tight limits')
+ import startup.i13
+ startup.i13.ct.pause = False
+ startup.i13.demo.all()
+
+
+def test_i16_startup():
+ import startup.i16
+ startup.i16.ct.pause = False
+ startup.i16.demo.all()
+
+
+def test_i21_startup_standard():
+ import startup.i21
+ startup.i21.ct.pause = False
+ startup.i21.demo.all()
+
+
+def test_i21_startup_bespoke():
+ import startup.i21
+ startup.i21.ct.pause = False
+ startup.i21.demo.i21()
+
+
+def test_sixcirle_api():
+ import startup.api.sixcircle
+ startup.api.sixcircle.demo_all()
+
diff --git a/script/__Lib/diffcalc-2.1/mock.py b/script/__Lib/diffcalc-2.1/mock.py
new file mode 100755
index 0000000..24799cf
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/mock.py
@@ -0,0 +1,2288 @@
+# mock.py
+# Test tools for mocking and patching.
+# Copyright (C) 2007-2012 Michael Foord & the mock team
+# E-mail: fuzzyman AT voidspace DOT org DOT uk
+
+# mock 0.8.0
+# http://www.voidspace.org.uk/python/mock/
+
+# Released subject to the BSD License
+# Please see http://www.voidspace.org.uk/python/license.shtml
+
+# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
+# Comments, suggestions and bug reports welcome.
+
+
+__all__ = (
+ 'Mock',
+ 'MagicMock',
+ 'mocksignature',
+ 'patch',
+ 'sentinel',
+ 'DEFAULT',
+ 'ANY',
+ 'call',
+ 'create_autospec',
+ 'FILTER_DIR',
+ 'NonCallableMock',
+ 'NonCallableMagicMock',
+)
+
+
+__version__ = '0.8.0'
+
+
+import pprint
+import sys
+
+try:
+ import inspect
+except ImportError:
+ # for alternative platforms that
+ # may not have inspect
+ inspect = None
+
+try:
+ from functools import wraps
+except ImportError:
+ # Python 2.4 compatibility
+ def wraps(original):
+ def inner(f):
+ f.__name__ = original.__name__
+ f.__doc__ = original.__doc__
+ f.__module__ = original.__module__
+ return f
+ return inner
+
+try:
+ unicode
+except NameError:
+ # Python 3
+ basestring = unicode = str
+
+try:
+ long
+except NameError:
+ # Python 3
+ long = int
+
+try:
+ BaseException
+except NameError:
+ # Python 2.4 compatibility
+ BaseException = Exception
+
+try:
+ next
+except NameError:
+ def next(obj):
+ return obj.next()
+
+
+BaseExceptions = (BaseException,)
+if 'java' in sys.platform:
+ # jython
+ import java
+ BaseExceptions = (BaseException, java.lang.Throwable)
+
+try:
+ _isidentifier = str.isidentifier
+except AttributeError:
+ # Python 2.X
+ import keyword
+ import re
+ regex = re.compile(r'^[a-z_][a-z0-9_]*$', re.I)
+ def _isidentifier(string):
+ if string in keyword.kwlist:
+ return False
+ return regex.match(string)
+
+
+inPy3k = sys.version_info[0] == 3
+
+# Needed to work around Python 3 bug where use of "super" interferes with
+# defining __class__ as a descriptor
+_super = super
+
+self = 'im_self'
+builtin = '__builtin__'
+if inPy3k:
+ self = '__self__'
+ builtin = 'builtins'
+
+FILTER_DIR = True
+
+
+def _is_instance_mock(obj):
+ # can't use isinstance on Mock objects because they override __class__
+ # The base class for all mocks is NonCallableMock
+ return issubclass(type(obj), NonCallableMock)
+
+
+def _is_exception(obj):
+ return (
+ isinstance(obj, BaseExceptions) or
+ isinstance(obj, ClassTypes) and issubclass(obj, BaseExceptions)
+ )
+
+
+class _slotted(object):
+ __slots__ = ['a']
+
+
+DescriptorTypes = (
+ type(_slotted.a),
+ property,
+)
+
+
+# getsignature and mocksignature heavily "inspired" by
+# the decorator module: http://pypi.python.org/pypi/decorator/
+# by Michele Simionato
+
+def _getsignature(func, skipfirst):
+ if inspect is None:
+ raise ImportError('inspect module not available')
+
+ if inspect.isclass(func):
+ func = func.__init__
+ # will have a self arg
+ skipfirst = True
+ elif not (inspect.ismethod(func) or inspect.isfunction(func)):
+ func = func.__call__
+
+ regargs, varargs, varkwargs, defaults = inspect.getargspec(func)
+
+ # instance methods need to lose the self argument
+ if getattr(func, self, None) is not None:
+ regargs = regargs[1:]
+
+ _msg = ("_mock_ is a reserved argument name, can't mock signatures using "
+ "_mock_")
+ assert '_mock_' not in regargs, _msg
+ if varargs is not None:
+ assert '_mock_' not in varargs, _msg
+ if varkwargs is not None:
+ assert '_mock_' not in varkwargs, _msg
+ if skipfirst:
+ regargs = regargs[1:]
+
+ signature = inspect.formatargspec(regargs, varargs, varkwargs, defaults,
+ formatvalue=lambda value: "")
+ return signature[1:-1], func
+
+
+def _getsignature2(func, skipfirst, instance=False):
+ if inspect is None:
+ raise ImportError('inspect module not available')
+
+ if isinstance(func, ClassTypes) and not instance:
+ try:
+ func = func.__init__
+ except AttributeError:
+ return
+ skipfirst = True
+ elif not isinstance(func, FunctionTypes):
+ # for classes where instance is True we end up here too
+ try:
+ func = func.__call__
+ except AttributeError:
+ return
+
+ try:
+ regargs, varargs, varkwargs, defaults = inspect.getargspec(func)
+ except TypeError:
+ # C function / method, possibly inherited object().__init__
+ return
+
+ # instance methods and classmethods need to lose the self argument
+ if getattr(func, self, None) is not None:
+ regargs = regargs[1:]
+ if skipfirst:
+ # this condition and the above one are never both True - why?
+ regargs = regargs[1:]
+
+ signature = inspect.formatargspec(regargs, varargs, varkwargs, defaults,
+ formatvalue=lambda value: "")
+ return signature[1:-1], func
+
+
+def _check_signature(func, mock, skipfirst, instance=False):
+ if not _callable(func):
+ return
+
+ result = _getsignature2(func, skipfirst, instance)
+ if result is None:
+ return
+ signature, func = result
+
+ # can't use self because "self" is common as an argument name
+ # unfortunately even not in the first place
+ src = "lambda _mock_self, %s: None" % signature
+ checksig = eval(src, {})
+ _copy_func_details(func, checksig)
+ type(mock)._mock_check_sig = checksig
+
+
+def _copy_func_details(func, funcopy):
+ funcopy.__name__ = func.__name__
+ funcopy.__doc__ = func.__doc__
+ #funcopy.__dict__.update(func.__dict__)
+ funcopy.__module__ = func.__module__
+ if not inPy3k:
+ funcopy.func_defaults = func.func_defaults
+ return
+ funcopy.__defaults__ = func.__defaults__
+ funcopy.__kwdefaults__ = func.__kwdefaults__
+
+
+def _callable(obj):
+ if isinstance(obj, ClassTypes):
+ return True
+ if getattr(obj, '__call__', None) is not None:
+ return True
+ return False
+
+
+def _is_list(obj):
+ # checks for list or tuples
+ # XXXX badly named!
+ return type(obj) in (list, tuple)
+
+
+def _instance_callable(obj):
+ """Given an object, return True if the object is callable.
+ For classes, return True if instances would be callable."""
+ if not isinstance(obj, ClassTypes):
+ # already an instance
+ return getattr(obj, '__call__', None) is not None
+
+ klass = obj
+ # uses __bases__ instead of __mro__ so that we work with old style classes
+ if klass.__dict__.get('__call__') is not None:
+ return True
+
+ for base in klass.__bases__:
+ if _instance_callable(base):
+ return True
+ return False
+
+
+def _set_signature(mock, original, instance=False):
+ # creates a function with signature (*args, **kwargs) that delegates to a
+ # mock. It still does signature checking by calling a lambda with the same
+ # signature as the original. This is effectively mocksignature2.
+ if not _callable(original):
+ return
+
+ skipfirst = isinstance(original, ClassTypes)
+ result = _getsignature2(original, skipfirst, instance)
+ if result is None:
+ # was a C function (e.g. object().__init__ ) that can't be mocked
+ return
+
+ signature, func = result
+
+ src = "lambda %s: None" % signature
+ context = {'_mock_': mock}
+ checksig = eval(src, context)
+ _copy_func_details(func, checksig)
+
+ name = original.__name__
+ if not _isidentifier(name):
+ name = 'funcopy'
+ context = {'checksig': checksig, 'mock': mock}
+ src = """def %s(*args, **kwargs):
+ checksig(*args, **kwargs)
+ return mock(*args, **kwargs)""" % name
+ exec (src, context)
+ funcopy = context[name]
+ _setup_func(funcopy, mock)
+ return funcopy
+
+
+def mocksignature(func, mock=None, skipfirst=False):
+ """
+ mocksignature(func, mock=None, skipfirst=False)
+
+ Create a new function with the same signature as `func` that delegates
+ to `mock`. If `skipfirst` is True the first argument is skipped, useful
+ for methods where `self` needs to be omitted from the new function.
+
+ If you don't pass in a `mock` then one will be created for you.
+
+ The mock is set as the `mock` attribute of the returned function for easy
+ access.
+
+ Functions returned by `mocksignature` have many of the same attributes
+ and assert methods as a mock object.
+
+ `mocksignature` can also be used with classes. It copies the signature of
+ the `__init__` method.
+
+ When used with callable objects (instances) it copies the signature of the
+ `__call__` method.
+ """
+ if mock is None:
+ mock = Mock()
+ signature, func = _getsignature(func, skipfirst)
+ src = "lambda %(signature)s: _mock_(%(signature)s)" % {
+ 'signature': signature
+ }
+
+ funcopy = eval(src, dict(_mock_=mock))
+ _copy_func_details(func, funcopy)
+ _setup_func(funcopy, mock)
+ return funcopy
+
+
+def _setup_func(funcopy, mock):
+ funcopy.mock = mock
+
+ # can't use isinstance with mocks
+ if not _is_instance_mock(mock):
+ return
+
+ def assert_called_with(*args, **kwargs):
+ return mock.assert_called_with(*args, **kwargs)
+ def assert_called_once_with(*args, **kwargs):
+ return mock.assert_called_once_with(*args, **kwargs)
+ def assert_has_calls(*args, **kwargs):
+ return mock.assert_has_calls(*args, **kwargs)
+ def assert_any_call(*args, **kwargs):
+ return mock.assert_any_call(*args, **kwargs)
+ def reset_mock():
+ funcopy.method_calls = _CallList()
+ funcopy.mock_calls = _CallList()
+ mock.reset_mock()
+ ret = funcopy.return_value
+ if _is_instance_mock(ret) and not ret is mock:
+ ret.reset_mock()
+
+ funcopy.called = False
+ funcopy.call_count = 0
+ funcopy.call_args = None
+ funcopy.call_args_list = _CallList()
+ funcopy.method_calls = _CallList()
+ funcopy.mock_calls = _CallList()
+
+ funcopy.return_value = mock.return_value
+ funcopy.side_effect = mock.side_effect
+ funcopy._mock_children = mock._mock_children
+
+ funcopy.assert_called_with = assert_called_with
+ funcopy.assert_called_once_with = assert_called_once_with
+ funcopy.assert_has_calls = assert_has_calls
+ funcopy.assert_any_call = assert_any_call
+ funcopy.reset_mock = reset_mock
+
+ mock._mock_signature = funcopy
+
+
+def _is_magic(name):
+ return '__%s__' % name[2:-2] == name
+
+
+class _SentinelObject(object):
+ "A unique, named, sentinel object."
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return 'sentinel.%s' % self.name
+
+
+class _Sentinel(object):
+ """Access attributes to return a named object, usable as a sentinel."""
+ def __init__(self):
+ self._sentinels = {}
+
+ def __getattr__(self, name):
+ if name == '__bases__':
+ # Without this help(mock) raises an exception
+ raise AttributeError
+ return self._sentinels.setdefault(name, _SentinelObject(name))
+
+
+sentinel = _Sentinel()
+
+DEFAULT = sentinel.DEFAULT
+
+
+class OldStyleClass:
+ pass
+ClassType = type(OldStyleClass)
+
+
+def _copy(value):
+ if type(value) in (dict, list, tuple, set):
+ return type(value)(value)
+ return value
+
+
+ClassTypes = (type,)
+if not inPy3k:
+ ClassTypes = (type, ClassType)
+
+_allowed_names = set(
+ [
+ 'return_value', '_mock_return_value', 'side_effect',
+ '_mock_side_effect', '_mock_parent', '_mock_new_parent',
+ '_mock_name', '_mock_new_name'
+ ]
+)
+
+
+def _mock_signature_property(name):
+ _allowed_names.add(name)
+ _the_name = '_mock_' + name
+ def _get(self, name=name, _the_name=_the_name):
+ sig = self._mock_signature
+ if sig is None:
+ return getattr(self, _the_name)
+ return getattr(sig, name)
+ def _set(self, value, name=name, _the_name=_the_name):
+ sig = self._mock_signature
+ if sig is None:
+ self.__dict__[_the_name] = value
+ else:
+ setattr(sig, name, value)
+
+ return property(_get, _set)
+
+
+
+class _CallList(list):
+
+ def __contains__(self, value):
+ if not isinstance(value, list):
+ return list.__contains__(self, value)
+ len_value = len(value)
+ len_self = len(self)
+ if len_value > len_self:
+ return False
+
+ for i in range(0, len_self - len_value + 1):
+ sub_list = self[i:i+len_value]
+ if sub_list == value:
+ return True
+ return False
+
+ def __repr__(self):
+ return pprint.pformat(list(self))
+
+
+def _check_and_set_parent(parent, value, name, new_name):
+ if not _is_instance_mock(value):
+ return False
+ if ((value._mock_name or value._mock_new_name) or
+ (value._mock_parent is not None) or
+ (value._mock_new_parent is not None)):
+ return False
+
+ _parent = parent
+ while _parent is not None:
+ # setting a mock (value) as a child or return value of itself
+ # should not modify the mock
+ if _parent is value:
+ return False
+ _parent = _parent._mock_new_parent
+
+ if new_name:
+ value._mock_new_parent = parent
+ value._mock_new_name = new_name
+ if name:
+ value._mock_parent = parent
+ value._mock_name = name
+ return True
+
+
+
+class Base(object):
+ _mock_return_value = DEFAULT
+ _mock_side_effect = None
+ def __init__(self, *args, **kwargs):
+ pass
+
+
+
+class NonCallableMock(Base):
+ """A non-callable version of `Mock`"""
+
+ def __new__(cls, *args, **kw):
+ # every instance has its own class
+ # so we can create magic methods on the
+ # class without stomping on other mocks
+ new = type(cls.__name__, (cls,), {'__doc__': cls.__doc__})
+ instance = object.__new__(new)
+ return instance
+
+
+ def __init__(
+ self, spec=None, wraps=None, name=None, spec_set=None,
+ parent=None, _spec_state=None, _new_name='', _new_parent=None,
+ **kwargs
+ ):
+ if _new_parent is None:
+ _new_parent = parent
+
+ __dict__ = self.__dict__
+ __dict__['_mock_parent'] = parent
+ __dict__['_mock_name'] = name
+ __dict__['_mock_new_name'] = _new_name
+ __dict__['_mock_new_parent'] = _new_parent
+
+ if spec_set is not None:
+ spec = spec_set
+ spec_set = True
+
+ self._mock_add_spec(spec, spec_set)
+
+ __dict__['_mock_children'] = {}
+ __dict__['_mock_wraps'] = wraps
+ __dict__['_mock_signature'] = None
+
+ __dict__['_mock_called'] = False
+ __dict__['_mock_call_args'] = None
+ __dict__['_mock_call_count'] = 0
+ __dict__['_mock_call_args_list'] = _CallList()
+ __dict__['_mock_mock_calls'] = _CallList()
+
+ __dict__['method_calls'] = _CallList()
+
+ if kwargs:
+ self.configure_mock(**kwargs)
+
+ _super(NonCallableMock, self).__init__(
+ spec, wraps, name, spec_set, parent,
+ _spec_state
+ )
+
+
+ def attach_mock(self, mock, attribute):
+ """
+ Attach a mock as an attribute of this one, replacing its name and
+ parent. Calls to the attached mock will be recorded in the
+ `method_calls` and `mock_calls` attributes of this one."""
+ mock._mock_parent = None
+ mock._mock_new_parent = None
+ mock._mock_name = ''
+ mock._mock_new_name = None
+
+ setattr(self, attribute, mock)
+
+
+ def mock_add_spec(self, spec, spec_set=False):
+ """Add a spec to a mock. `spec` can either be an object or a
+ list of strings. Only attributes on the `spec` can be fetched as
+ attributes from the mock.
+
+ If `spec_set` is True then only attributes on the spec can be set."""
+ self._mock_add_spec(spec, spec_set)
+
+
+ def _mock_add_spec(self, spec, spec_set):
+ _spec_class = None
+
+ if spec is not None and not _is_list(spec):
+ if isinstance(spec, ClassTypes):
+ _spec_class = spec
+ else:
+ _spec_class = _get_class(spec)
+
+ spec = dir(spec)
+
+ __dict__ = self.__dict__
+ __dict__['_spec_class'] = _spec_class
+ __dict__['_spec_set'] = spec_set
+ __dict__['_mock_methods'] = spec
+
+
+ def __get_return_value(self):
+ ret = self._mock_return_value
+ if self._mock_signature is not None:
+ ret = self._mock_signature.return_value
+
+ if ret is DEFAULT:
+ ret = self._get_child_mock(
+ _new_parent=self, _new_name='()'
+ )
+ self.return_value = ret
+ return ret
+
+
+ def __set_return_value(self, value):
+ if self._mock_signature is not None:
+ self._mock_signature.return_value = value
+ else:
+ self._mock_return_value = value
+ _check_and_set_parent(self, value, None, '()')
+
+ __return_value_doc = "The value to be returned when the mock is called."
+ return_value = property(__get_return_value, __set_return_value,
+ __return_value_doc)
+
+
+ @property
+ def __class__(self):
+ if self._spec_class is None:
+ return type(self)
+ return self._spec_class
+
+ called = _mock_signature_property('called')
+ call_count = _mock_signature_property('call_count')
+ call_args = _mock_signature_property('call_args')
+ call_args_list = _mock_signature_property('call_args_list')
+ mock_calls = _mock_signature_property('mock_calls')
+
+
+ def __get_side_effect(self):
+ sig = self._mock_signature
+ if sig is None:
+ return self._mock_side_effect
+ return sig.side_effect
+
+ def __set_side_effect(self, value):
+ value = _try_iter(value)
+ sig = self._mock_signature
+ if sig is None:
+ self._mock_side_effect = value
+ else:
+ sig.side_effect = value
+
+ side_effect = property(__get_side_effect, __set_side_effect)
+
+
+ def reset_mock(self):
+ "Restore the mock object to its initial state."
+ self.called = False
+ self.call_args = None
+ self.call_count = 0
+ self.mock_calls = _CallList()
+ self.call_args_list = _CallList()
+ self.method_calls = _CallList()
+
+ for child in self._mock_children.values():
+ child.reset_mock()
+
+ ret = self._mock_return_value
+ if _is_instance_mock(ret) and ret is not self:
+ ret.reset_mock()
+
+
+ def configure_mock(self, **kwargs):
+ """Set attributes on the mock through keyword arguments.
+
+ Attributes plus return values and side effects can be set on child
+ mocks using standard dot notation and unpacking a dictionary in the
+ method call:
+
+ >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
+ >>> mock.configure_mock(**attrs)"""
+ for arg, val in sorted(kwargs.items(),
+ # we sort on the number of dots so that
+ # attributes are set before we set attributes on
+ # attributes
+ key=lambda entry: entry[0].count('.')):
+ args = arg.split('.')
+ final = args.pop()
+ obj = self
+ for entry in args:
+ obj = getattr(obj, entry)
+ setattr(obj, final, val)
+
+
+ def __getattr__(self, name):
+ if name == '_mock_methods':
+ raise AttributeError(name)
+ elif self._mock_methods is not None:
+ if name not in self._mock_methods or name in _all_magics:
+ raise AttributeError("Mock object has no attribute %r" % name)
+ elif _is_magic(name):
+ raise AttributeError(name)
+
+ result = self._mock_children.get(name)
+ if result is None:
+ wraps = None
+ if self._mock_wraps is not None:
+ # XXXX should we get the attribute without triggering code
+ # execution?
+ wraps = getattr(self._mock_wraps, name)
+
+ result = self._get_child_mock(
+ parent=self, name=name, wraps=wraps, _new_name=name,
+ _new_parent=self
+ )
+ self._mock_children[name] = result
+
+ elif isinstance(result, _SpecState):
+ result = create_autospec(
+ result.spec, result.spec_set, result.instance,
+ result.parent, result.name
+ )
+ self._mock_children[name] = result
+
+ return result
+
+
+ def __repr__(self):
+ _name_list = [self._mock_new_name]
+ _parent = self._mock_new_parent
+ last = self
+
+ dot = '.'
+ if _name_list == ['()']:
+ dot = ''
+ seen = set()
+ while _parent is not None:
+ last = _parent
+
+ _name_list.append(_parent._mock_new_name + dot)
+ dot = '.'
+ if _parent._mock_new_name == '()':
+ dot = ''
+
+ _parent = _parent._mock_new_parent
+
+ # use ids here so as not to call __hash__ on the mocks
+ if id(_parent) in seen:
+ break
+ seen.add(id(_parent))
+
+ _name_list = list(reversed(_name_list))
+ _first = last._mock_name or 'mock'
+ if len(_name_list) > 1:
+ if _name_list[1] not in ('()', '().'):
+ _first += '.'
+ _name_list[0] = _first
+ name = ''.join(_name_list)
+
+ name_string = ''
+ if name not in ('mock', 'mock.'):
+ name_string = ' name=%r' % name
+
+ spec_string = ''
+ if self._spec_class is not None:
+ spec_string = ' spec=%r'
+ if self._spec_set:
+ spec_string = ' spec_set=%r'
+ spec_string = spec_string % self._spec_class.__name__
+ return "<%s%s%s id='%s'>" % (
+ type(self).__name__,
+ name_string,
+ spec_string,
+ id(self)
+ )
+
+
+ def __dir__(self):
+ """Filter the output of `dir(mock)` to only useful members.
+ XXXX
+ """
+ extras = self._mock_methods or []
+ from_type = dir(type(self))
+ from_dict = list(self.__dict__)
+
+ if FILTER_DIR:
+ from_type = [e for e in from_type if not e.startswith('_')]
+ from_dict = [e for e in from_dict if not e.startswith('_') or
+ _is_magic(e)]
+ return sorted(set(extras + from_type + from_dict +
+ list(self._mock_children)))
+
+
+ def __setattr__(self, name, value):
+ if name in _allowed_names:
+ # property setters go through here
+ return object.__setattr__(self, name, value)
+ elif (self._spec_set and self._mock_methods is not None and
+ name not in self._mock_methods and
+ name not in self.__dict__):
+ raise AttributeError("Mock object has no attribute '%s'" % name)
+ elif name in _unsupported_magics:
+ msg = 'Attempting to set unsupported magic method %r.' % name
+ raise AttributeError(msg)
+ elif name in _all_magics:
+ if self._mock_methods is not None and name not in self._mock_methods:
+ raise AttributeError("Mock object has no attribute '%s'" % name)
+
+ if not _is_instance_mock(value):
+ setattr(type(self), name, _get_method(name, value))
+ original = value
+ real = lambda *args, **kw: original(self, *args, **kw)
+ value = mocksignature(value, real, skipfirst=True)
+ else:
+ # only set _new_name and not name so that mock_calls is tracked
+ # but not method calls
+ _check_and_set_parent(self, value, None, name)
+ setattr(type(self), name, value)
+ else:
+ if _check_and_set_parent(self, value, name, name):
+ self._mock_children[name] = value
+ return object.__setattr__(self, name, value)
+
+
+ def __delattr__(self, name):
+ if name in _all_magics and name in type(self).__dict__:
+ delattr(type(self), name)
+ if name not in self.__dict__:
+ # for magic methods that are still MagicProxy objects and
+ # not set on the instance itself
+ return
+
+ return object.__delattr__(self, name)
+
+
+ def _format_mock_call_signature(self, args, kwargs):
+ name = self._mock_name or 'mock'
+ return _format_call_signature(name, args, kwargs)
+
+
+ def _format_mock_failure_message(self, args, kwargs):
+ message = 'Expected call: %s\nActual call: %s'
+ expected_string = self._format_mock_call_signature(args, kwargs)
+ call_args = self.call_args
+ if len(call_args) == 3:
+ call_args = call_args[1:]
+ actual_string = self._format_mock_call_signature(*call_args)
+ return message % (expected_string, actual_string)
+
+
+ def assert_called_with(_mock_self, *args, **kwargs):
+ """assert that the mock was called with the specified arguments.
+
+ Raises an AssertionError if the args and keyword args passed in are
+ different to the last call to the mock."""
+ self = _mock_self
+ if self.call_args is None:
+ expected = self._format_mock_call_signature(args, kwargs)
+ raise AssertionError('Expected call: %s\nNot called' % (expected,))
+
+ if self.call_args != (args, kwargs):
+ msg = self._format_mock_failure_message(args, kwargs)
+ raise AssertionError(msg)
+
+
+ def assert_called_once_with(_mock_self, *args, **kwargs):
+ """assert that the mock was called exactly once and with the specified
+ arguments."""
+ self = _mock_self
+ if not self.call_count == 1:
+ msg = ("Expected to be called once. Called %s times." %
+ self.call_count)
+ raise AssertionError(msg)
+ return self.assert_called_with(*args, **kwargs)
+
+
+ def assert_has_calls(self, calls, any_order=False):
+ """assert the mock has been called with the specified calls.
+ The `mock_calls` list is checked for the calls.
+
+ If `any_order` is False (the default) then the calls must be
+ sequential. There can be extra calls before or after the
+ specified calls.
+
+ If `any_order` is True then the calls can be in any order, but
+ they must all appear in `mock_calls`."""
+ if not any_order:
+ if calls not in self.mock_calls:
+ raise AssertionError(
+ 'Calls not found.\nExpected: %r\n'
+ 'Actual: %r' % (calls, self.mock_calls)
+ )
+ return
+
+ all_calls = list(self.mock_calls)
+
+ not_found = []
+ for kall in calls:
+ try:
+ all_calls.remove(kall)
+ except ValueError:
+ not_found.append(kall)
+ if not_found:
+ raise AssertionError(
+ '%r not all found in call list' % (tuple(not_found),)
+ )
+
+
+ def assert_any_call(self, *args, **kwargs):
+ """assert the mock has been called with the specified arguments.
+
+ The assert passes if the mock has *ever* been called, unlike
+ `assert_called_with` and `assert_called_once_with` that only pass if
+ the call is the most recent one."""
+ kall = call(*args, **kwargs)
+ if kall not in self.call_args_list:
+ expected_string = self._format_mock_call_signature(args, kwargs)
+ raise AssertionError(
+ '%s call not found' % expected_string
+ )
+
+
+ def _get_child_mock(self, **kw):
+ """Create the child mocks for attributes and return value.
+ By default child mocks will be the same type as the parent.
+ Subclasses of Mock may want to override this to customize the way
+ child mocks are made.
+
+ For non-callable mocks the callable variant will be used (rather than
+ any custom subclass)."""
+ _type = type(self)
+ if not issubclass(_type, CallableMixin):
+ if issubclass(_type, NonCallableMagicMock):
+ klass = MagicMock
+ elif issubclass(_type, NonCallableMock) :
+ klass = Mock
+ else:
+ klass = _type.__mro__[1]
+ return klass(**kw)
+
+
+
+def _try_iter(obj):
+ if obj is None:
+ return obj
+ if _is_exception(obj):
+ return obj
+ if _callable(obj):
+ return obj
+ try:
+ return iter(obj)
+ except TypeError:
+ # XXXX backwards compatibility
+ # but this will blow up on first call - so maybe we should fail early?
+ return obj
+
+
+
+class CallableMixin(Base):
+
+ def __init__(self, spec=None, side_effect=None, return_value=DEFAULT,
+ wraps=None, name=None, spec_set=None, parent=None,
+ _spec_state=None, _new_name='', _new_parent=None, **kwargs):
+ self.__dict__['_mock_return_value'] = return_value
+
+ _super(CallableMixin, self).__init__(
+ spec, wraps, name, spec_set, parent,
+ _spec_state, _new_name, _new_parent, **kwargs
+ )
+
+ self.side_effect = side_effect
+
+
+ def _mock_check_sig(self, *args, **kwargs):
+ # stub method that can be replaced with one with a specific signature
+ pass
+
+
+ def __call__(_mock_self, *args, **kwargs):
+ # can't use self in-case a function / method we are mocking uses self
+ # in the signature
+ _mock_self._mock_check_sig(*args, **kwargs)
+ return _mock_self._mock_call(*args, **kwargs)
+
+
+ def _mock_call(_mock_self, *args, **kwargs):
+ self = _mock_self
+ self.called = True
+ self.call_count += 1
+ self.call_args = _Call((args, kwargs), two=True)
+ self.call_args_list.append(_Call((args, kwargs), two=True))
+
+ _new_name = self._mock_new_name
+ _new_parent = self._mock_new_parent
+ self.mock_calls.append(_Call(('', args, kwargs)))
+
+ seen = set()
+ skip_next_dot = _new_name == '()'
+ do_method_calls = self._mock_parent is not None
+ name = self._mock_name
+ while _new_parent is not None:
+ this_mock_call = _Call((_new_name, args, kwargs))
+ if _new_parent._mock_new_name:
+ dot = '.'
+ if skip_next_dot:
+ dot = ''
+
+ skip_next_dot = False
+ if _new_parent._mock_new_name == '()':
+ skip_next_dot = True
+
+ _new_name = _new_parent._mock_new_name + dot + _new_name
+
+ if do_method_calls:
+ if _new_name == name:
+ this_method_call = this_mock_call
+ else:
+ this_method_call = _Call((name, args, kwargs))
+ _new_parent.method_calls.append(this_method_call)
+
+ do_method_calls = _new_parent._mock_parent is not None
+ if do_method_calls:
+ name = _new_parent._mock_name + '.' + name
+
+ _new_parent.mock_calls.append(this_mock_call)
+ _new_parent = _new_parent._mock_new_parent
+
+ # use ids here so as not to call __hash__ on the mocks
+ _new_parent_id = id(_new_parent)
+ if _new_parent_id in seen:
+ break
+ seen.add(_new_parent_id)
+
+ ret_val = DEFAULT
+ effect = self.side_effect
+ if effect is not None:
+ if _is_exception(effect):
+ raise effect
+
+ if not _callable(effect):
+ return next(effect)
+
+ ret_val = effect(*args, **kwargs)
+ if ret_val is DEFAULT:
+ ret_val = self.return_value
+
+ if (self._mock_wraps is not None and
+ self._mock_return_value is DEFAULT):
+ return self._mock_wraps(*args, **kwargs)
+ if ret_val is DEFAULT:
+ ret_val = self.return_value
+ return ret_val
+
+
+
+class Mock(CallableMixin, NonCallableMock):
+ """
+ Create a new `Mock` object. `Mock` takes several optional arguments
+ that specify the behaviour of the Mock object:
+
+ * `spec`: This can be either a list of strings or an existing object (a
+ class or instance) that acts as the specification for the mock object. If
+ you pass in an object then a list of strings is formed by calling dir on
+ the object (excluding unsupported magic attributes and methods). Accessing
+ any attribute not in this list will raise an `AttributeError`.
+
+ If `spec` is an object (rather than a list of strings) then
+ `mock.__class__` returns the class of the spec object. This allows mocks
+ to pass `isinstance` tests.
+
+ * `spec_set`: A stricter variant of `spec`. If used, attempting to *set*
+ or get an attribute on the mock that isn't on the object passed as
+ `spec_set` will raise an `AttributeError`.
+
+ * `side_effect`: A function to be called whenever the Mock is called. See
+ the `side_effect` attribute. Useful for raising exceptions or
+ dynamically changing return values. The function is called with the same
+ arguments as the mock, and unless it returns `DEFAULT`, the return
+ value of this function is used as the return value.
+
+ Alternatively `side_effect` can be an exception class or instance. In
+ this case the exception will be raised when the mock is called.
+
+ If `side_effect` is an iterable then each call to the mock will return
+ the next value from the iterable.
+
+ * `return_value`: The value returned when the mock is called. By default
+ this is a new Mock (created on first access). See the
+ `return_value` attribute.
+
+ * `wraps`: Item for the mock object to wrap. If `wraps` is not None
+ then calling the Mock will pass the call through to the wrapped object
+ (returning the real result and ignoring `return_value`). Attribute
+ access on the mock will return a Mock object that wraps the corresponding
+ attribute of the wrapped object (so attempting to access an attribute that
+ doesn't exist will raise an `AttributeError`).
+
+ If the mock has an explicit `return_value` set then calls are not passed
+ to the wrapped object and the `return_value` is returned instead.
+
+ * `name`: If the mock has a name then it will be used in the repr of the
+ mock. This can be useful for debugging. The name is propagated to child
+ mocks.
+
+ Mocks can also be called with arbitrary keyword arguments. These will be
+ used to set attributes on the mock after it is created.
+ """
+
+
+
+def _dot_lookup(thing, comp, import_path):
+ try:
+ return getattr(thing, comp)
+ except AttributeError:
+ __import__(import_path)
+ return getattr(thing, comp)
+
+
+def _importer(target):
+ components = target.split('.')
+ import_path = components.pop(0)
+ thing = __import__(import_path)
+
+ for comp in components:
+ import_path += ".%s" % comp
+ thing = _dot_lookup(thing, comp, import_path)
+ return thing
+
+
+def _is_started(patcher):
+ # XXXX horrible
+ return hasattr(patcher, 'is_local')
+
+
+class _patch(object):
+
+ attribute_name = None
+
+ def __init__(
+ self, getter, attribute, new, spec, create,
+ mocksignature, spec_set, autospec, new_callable, kwargs
+ ):
+ if new_callable is not None:
+ if new is not DEFAULT:
+ raise ValueError(
+ "Cannot use 'new' and 'new_callable' together"
+ )
+ if autospec is not False:
+ raise ValueError(
+ "Cannot use 'autospec' and 'new_callable' together"
+ )
+
+ self.getter = getter
+ self.attribute = attribute
+ self.new = new
+ self.new_callable = new_callable
+ self.spec = spec
+ self.create = create
+ self.has_local = False
+ self.mocksignature = mocksignature
+ self.spec_set = spec_set
+ self.autospec = autospec
+ self.kwargs = kwargs
+ self.additional_patchers = []
+
+
+ def copy(self):
+ patcher = _patch(
+ self.getter, self.attribute, self.new, self.spec,
+ self.create, self.mocksignature, self.spec_set,
+ self.autospec, self.new_callable, self.kwargs
+ )
+ patcher.attribute_name = self.attribute_name
+ patcher.additional_patchers = [
+ p.copy() for p in self.additional_patchers
+ ]
+ return patcher
+
+
+ def __call__(self, func):
+ if isinstance(func, ClassTypes):
+ return self.decorate_class(func)
+ return self.decorate_callable(func)
+
+
+ def decorate_class(self, klass):
+ for attr in dir(klass):
+ if not attr.startswith(patch.TEST_PREFIX):
+ continue
+
+ attr_value = getattr(klass, attr)
+ if not hasattr(attr_value, "__call__"):
+ continue
+
+ patcher = self.copy()
+ setattr(klass, attr, patcher(attr_value))
+ return klass
+
+
+ def decorate_callable(self, func):
+ if hasattr(func, 'patchings'):
+ func.patchings.append(self)
+ return func
+
+ @wraps(func)
+ def patched(*args, **keywargs):
+ # don't use a with here (backwards compatability with Python 2.4)
+ extra_args = []
+ entered_patchers = []
+
+ # can't use try...except...finally because of Python 2.4
+ # compatibility
+ try:
+ try:
+ for patching in patched.patchings:
+ arg = patching.__enter__()
+ entered_patchers.append(patching)
+ if patching.attribute_name is not None:
+ keywargs.update(arg)
+ elif patching.new is DEFAULT:
+ extra_args.append(arg)
+
+ args += tuple(extra_args)
+ return func(*args, **keywargs)
+ except:
+ if (patching not in entered_patchers and
+ _is_started(patching)):
+ # the patcher may have been started, but an exception
+ # raised whilst entering one of its additional_patchers
+ entered_patchers.append(patching)
+ # re-raise the exception
+ raise
+ finally:
+ for patching in reversed(entered_patchers):
+ patching.__exit__()
+
+ patched.patchings = [self]
+ if hasattr(func, 'func_code'):
+ # not in Python 3
+ patched.compat_co_firstlineno = getattr(
+ func, "compat_co_firstlineno",
+ func.func_code.co_firstlineno
+ )
+ return patched
+
+
+ def get_original(self):
+ target = self.getter()
+ name = self.attribute
+
+ original = DEFAULT
+ local = False
+
+ try:
+ original = target.__dict__[name]
+ except (AttributeError, KeyError):
+ original = getattr(target, name, DEFAULT)
+ else:
+ local = True
+
+ if not self.create and original is DEFAULT:
+ raise AttributeError(
+ "%s does not have the attribute %r" % (target, name)
+ )
+ return original, local
+
+
+ def __enter__(self):
+ """Perform the patch."""
+ new, spec, spec_set = self.new, self.spec, self.spec_set
+ autospec, kwargs = self.autospec, self.kwargs
+ new_callable = self.new_callable
+ self.target = self.getter()
+
+ original, local = self.get_original()
+
+ if new is DEFAULT and autospec is False:
+ inherit = False
+ if spec_set == True:
+ spec_set = original
+ elif spec == True:
+ # set spec to the object we are replacing
+ spec = original
+
+ if (spec or spec_set) is not None:
+ if isinstance(original, ClassTypes):
+ # If we're patching out a class and there is a spec
+ inherit = True
+
+ Klass = MagicMock
+ _kwargs = {}
+ if new_callable is not None:
+ Klass = new_callable
+ elif (spec or spec_set) is not None:
+ if not _callable(spec or spec_set):
+ Klass = NonCallableMagicMock
+
+ if spec is not None:
+ _kwargs['spec'] = spec
+ if spec_set is not None:
+ _kwargs['spec_set'] = spec_set
+
+ # add a name to mocks
+ if (isinstance(Klass, type) and
+ issubclass(Klass, NonCallableMock) and self.attribute):
+ _kwargs['name'] = self.attribute
+
+ _kwargs.update(kwargs)
+ new = Klass(**_kwargs)
+
+ if inherit and _is_instance_mock(new):
+ # we can only tell if the instance should be callable if the
+ # spec is not a list
+ if (not _is_list(spec or spec_set) and not
+ _instance_callable(spec or spec_set)):
+ Klass = NonCallableMagicMock
+
+ _kwargs.pop('name')
+ new.return_value = Klass(_new_parent=new, _new_name='()',
+ **_kwargs)
+ elif autospec is not False:
+ # spec is ignored, new *must* be default, spec_set is treated
+ # as a boolean. Should we check spec is not None and that spec_set
+ # is a bool? mocksignature should also not be used. Should we
+ # check this?
+ if new is not DEFAULT:
+ raise TypeError(
+ "autospec creates the mock for you. Can't specify "
+ "autospec and new."
+ )
+ spec_set = bool(spec_set)
+ if autospec is True:
+ autospec = original
+
+ new = create_autospec(autospec, spec_set=spec_set,
+ _name=self.attribute, **kwargs)
+ elif kwargs:
+ # can't set keyword args when we aren't creating the mock
+ # XXXX If new is a Mock we could call new.configure_mock(**kwargs)
+ raise TypeError("Can't pass kwargs to a mock we aren't creating")
+
+ new_attr = new
+ if self.mocksignature:
+ new_attr = mocksignature(original, new)
+
+ self.temp_original = original
+ self.is_local = local
+ setattr(self.target, self.attribute, new_attr)
+ if self.attribute_name is not None:
+ extra_args = {}
+ if self.new is DEFAULT:
+ extra_args[self.attribute_name] = new
+ for patching in self.additional_patchers:
+ arg = patching.__enter__()
+ if patching.new is DEFAULT:
+ extra_args.update(arg)
+ return extra_args
+
+ return new
+
+
+ def __exit__(self, *_):
+ """Undo the patch."""
+ if not _is_started(self):
+ raise RuntimeError('stop called on unstarted patcher')
+
+ if self.is_local and self.temp_original is not DEFAULT:
+ setattr(self.target, self.attribute, self.temp_original)
+ else:
+ delattr(self.target, self.attribute)
+ if not self.create and not hasattr(self.target, self.attribute):
+ # needed for proxy objects like django settings
+ setattr(self.target, self.attribute, self.temp_original)
+
+ del self.temp_original
+ del self.is_local
+ del self.target
+ for patcher in reversed(self.additional_patchers):
+ if _is_started(patcher):
+ patcher.__exit__()
+
+ start = __enter__
+ stop = __exit__
+
+
+
+def _get_target(target):
+ try:
+ target, attribute = target.rsplit('.', 1)
+ except (TypeError, ValueError):
+ raise TypeError("Need a valid target to patch. You supplied: %r" %
+ (target,))
+ getter = lambda: _importer(target)
+ return getter, attribute
+
+
+def _patch_object(
+ target, attribute, new=DEFAULT, spec=None,
+ create=False, mocksignature=False, spec_set=None, autospec=False,
+ new_callable=None, **kwargs
+ ):
+ """
+ patch.object(target, attribute, new=DEFAULT, spec=None, create=False,
+ mocksignature=False, spec_set=None, autospec=False,
+ new_callable=None, **kwargs)
+
+ patch the named member (`attribute`) on an object (`target`) with a mock
+ object.
+
+ `patch.object` can be used as a decorator, class decorator or a context
+ manager. Arguments `new`, `spec`, `create`, `mocksignature`, `spec_set`,
+ `autospec` and `new_callable` have the same meaning as for `patch`. Like
+ `patch`, `patch.object` takes arbitrary keyword arguments for configuring
+ the mock object it creates.
+
+ When used as a class decorator `patch.object` honours `patch.TEST_PREFIX`
+ for choosing which methods to wrap.
+ """
+ getter = lambda: target
+ return _patch(
+ getter, attribute, new, spec, create, mocksignature,
+ spec_set, autospec, new_callable, kwargs
+ )
+
+
+def _patch_multiple(target, spec=None, create=False,
+ mocksignature=False, spec_set=None, autospec=False,
+ new_callable=None, **kwargs
+ ):
+ """Perform multiple patches in a single call. It takes the object to be
+ patched (either as an object or a string to fetch the object by importing)
+ and keyword arguments for the patches::
+
+ with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'):
+ ...
+
+ Use `DEFAULT` as the value if you want `patch.multiple` to create
+ mocks for you. In this case the created mocks are passed into a decorated
+ function by keyword, and a dictionary is returned when `patch.multiple` is
+ used as a context manager.
+
+ `patch.multiple` can be used as a decorator, class decorator or a context
+ manager. The arguments `spec`, `spec_set`, `create`, `mocksignature`,
+ `autospec` and `new_callable` have the same meaning as for `patch`. These
+ arguments will be applied to *all* patches done by `patch.multiple`.
+
+ When used as a class decorator `patch.multiple` honours `patch.TEST_PREFIX`
+ for choosing which methods to wrap.
+ """
+ if type(target) in (unicode, str):
+ getter = lambda: _importer(target)
+ else:
+ getter = lambda: target
+
+ if not kwargs:
+ raise ValueError(
+ 'Must supply at least one keyword argument with patch.multiple'
+ )
+ # need to wrap in a list for python 3, where items is a view
+ items = list(kwargs.items())
+ attribute, new = items[0]
+ patcher = _patch(
+ getter, attribute, new, spec, create, mocksignature, spec_set,
+ autospec, new_callable, {}
+ )
+ patcher.attribute_name = attribute
+ for attribute, new in items[1:]:
+ this_patcher = _patch(
+ getter, attribute, new, spec, create, mocksignature, spec_set,
+ autospec, new_callable, {}
+ )
+ this_patcher.attribute_name = attribute
+ patcher.additional_patchers.append(this_patcher)
+ return patcher
+
+
+def patch(
+ target, new=DEFAULT, spec=None, create=False,
+ mocksignature=False, spec_set=None, autospec=False,
+ new_callable=None, **kwargs
+ ):
+ """
+ `patch` acts as a function decorator, class decorator or a context
+ manager. Inside the body of the function or with statement, the `target`
+ (specified in the form `'package.module.ClassName'`) is patched
+ with a `new` object. When the function/with statement exits the patch is
+ undone.
+
+ The `target` is imported and the specified attribute patched with the new
+ object, so it must be importable from the environment you are calling the
+ decorator from. The target is imported when the decorated function is
+ executed, not at decoration time.
+
+ If `new` is omitted, then a new `MagicMock` is created and passed in as an
+ extra argument to the decorated function.
+
+ The `spec` and `spec_set` keyword arguments are passed to the `MagicMock`
+ if patch is creating one for you.
+
+ In addition you can pass `spec=True` or `spec_set=True`, which causes
+ patch to pass in the object being mocked as the spec/spec_set object.
+
+ `new_callable` allows you to specify a different class, or callable object,
+ that will be called to create the `new` object. By default `MagicMock` is
+ used.
+
+ A more powerful form of `spec` is `autospec`. If you set `autospec=True`
+ then the mock with be created with a spec from the object being replaced.
+ All attributes of the mock will also have the spec of the corresponding
+ attribute of the object being replaced. Methods and functions being mocked
+ will have their arguments checked and will raise a `TypeError` if they are
+ called with the wrong signature (similar to `mocksignature`). For mocks
+ replacing a class, their return value (the 'instance') will have the same
+ spec as the class.
+
+ Instead of `autospec=True` you can pass `autospec=some_object` to use an
+ arbitrary object as the spec instead of the one being replaced.
+
+ If `mocksignature` is True then the patch will be done with a function
+ created by mocking the one being replaced. If the object being replaced is
+ a class then the signature of `__init__` will be copied. If the object
+ being replaced is a callable object then the signature of `__call__` will
+ be copied.
+
+ By default `patch` will fail to replace attributes that don't exist. If
+ you pass in `create=True`, and the attribute doesn't exist, patch will
+ create the attribute for you when the patched function is called, and
+ delete it again afterwards. This is useful for writing tests against
+ attributes that your production code creates at runtime. It is off by by
+ default because it can be dangerous. With it switched on you can write
+ passing tests against APIs that don't actually exist!
+
+ Patch can be used as a `TestCase` class decorator. It works by
+ decorating each test method in the class. This reduces the boilerplate
+ code when your test methods share a common patchings set. `patch` finds
+ tests by looking for method names that start with `patch.TEST_PREFIX`.
+ By default this is `test`, which matches the way `unittest` finds tests.
+ You can specify an alternative prefix by setting `patch.TEST_PREFIX`.
+
+ Patch can be used as a context manager, with the with statement. Here the
+ patching applies to the indented block after the with statement. If you
+ use "as" then the patched object will be bound to the name after the
+ "as"; very useful if `patch` is creating a mock object for you.
+
+ `patch` takes arbitrary keyword arguments. These will be passed to
+ the `Mock` (or `new_callable`) on construction.
+
+ `patch.dict(...)`, `patch.multiple(...)` and `patch.object(...)` are
+ available for alternate use-cases.
+ """
+ getter, attribute = _get_target(target)
+ return _patch(
+ getter, attribute, new, spec, create, mocksignature,
+ spec_set, autospec, new_callable, kwargs
+ )
+
+
+class _patch_dict(object):
+ """
+ Patch a dictionary, or dictionary like object, and restore the dictionary
+ to its original state after the test.
+
+ `in_dict` can be a dictionary or a mapping like container. If it is a
+ mapping then it must at least support getting, setting and deleting items
+ plus iterating over keys.
+
+ `in_dict` can also be a string specifying the name of the dictionary, which
+ will then be fetched by importing it.
+
+ `values` can be a dictionary of values to set in the dictionary. `values`
+ can also be an iterable of `(key, value)` pairs.
+
+ If `clear` is True then the dictionary will be cleared before the new
+ values are set.
+
+ `patch.dict` can also be called with arbitrary keyword arguments to set
+ values in the dictionary::
+
+ with patch.dict('sys.modules', mymodule=Mock(), other_module=Mock()):
+ ...
+
+ `patch.dict` can be used as a context manager, decorator or class
+ decorator. When used as a class decorator `patch.dict` honours
+ `patch.TEST_PREFIX` for choosing which methods to wrap.
+ """
+
+ def __init__(self, in_dict, values=(), clear=False, **kwargs):
+ if isinstance(in_dict, basestring):
+ in_dict = _importer(in_dict)
+ self.in_dict = in_dict
+ # support any argument supported by dict(...) constructor
+ self.values = dict(values)
+ self.values.update(kwargs)
+ self.clear = clear
+ self._original = None
+
+
+ def __call__(self, f):
+ if isinstance(f, ClassTypes):
+ return self.decorate_class(f)
+ @wraps(f)
+ def _inner(*args, **kw):
+ self._patch_dict()
+ try:
+ return f(*args, **kw)
+ finally:
+ self._unpatch_dict()
+
+ return _inner
+
+
+ def decorate_class(self, klass):
+ for attr in dir(klass):
+ attr_value = getattr(klass, attr)
+ if (attr.startswith(patch.TEST_PREFIX) and
+ hasattr(attr_value, "__call__")):
+ decorator = _patch_dict(self.in_dict, self.values, self.clear)
+ decorated = decorator(attr_value)
+ setattr(klass, attr, decorated)
+ return klass
+
+
+ def __enter__(self):
+ """Patch the dict."""
+ self._patch_dict()
+
+
+ def _patch_dict(self):
+ values = self.values
+ in_dict = self.in_dict
+ clear = self.clear
+
+ try:
+ original = in_dict.copy()
+ except AttributeError:
+ # dict like object with no copy method
+ # must support iteration over keys
+ original = {}
+ for key in in_dict:
+ original[key] = in_dict[key]
+ self._original = original
+
+ if clear:
+ _clear_dict(in_dict)
+
+ try:
+ in_dict.update(values)
+ except AttributeError:
+ # dict like object with no update method
+ for key in values:
+ in_dict[key] = values[key]
+
+
+ def _unpatch_dict(self):
+ in_dict = self.in_dict
+ original = self._original
+
+ _clear_dict(in_dict)
+
+ try:
+ in_dict.update(original)
+ except AttributeError:
+ for key in original:
+ in_dict[key] = original[key]
+
+
+ def __exit__(self, *args):
+ """Unpatch the dict."""
+ self._unpatch_dict()
+ return False
+
+ start = __enter__
+ stop = __exit__
+
+
+def _clear_dict(in_dict):
+ try:
+ in_dict.clear()
+ except AttributeError:
+ keys = list(in_dict)
+ for key in keys:
+ del in_dict[key]
+
+
+patch.object = _patch_object
+patch.dict = _patch_dict
+patch.multiple = _patch_multiple
+patch.TEST_PREFIX = 'test'
+
+magic_methods = (
+ "lt le gt ge eq ne "
+ "getitem setitem delitem "
+ "len contains iter "
+ "hash str sizeof "
+ "enter exit "
+ "divmod neg pos abs invert "
+ "complex int float index "
+ "trunc floor ceil "
+)
+
+numerics = "add sub mul div floordiv mod lshift rshift and xor or pow "
+inplace = ' '.join('i%s' % n for n in numerics.split())
+right = ' '.join('r%s' % n for n in numerics.split())
+extra = ''
+if inPy3k:
+ extra = 'bool next '
+else:
+ extra = 'unicode long nonzero oct hex truediv rtruediv '
+
+# not including __prepare__, __instancecheck__, __subclasscheck__
+# (as they are metaclass methods)
+# __del__ is not supported at all as it causes problems if it exists
+
+_non_defaults = set('__%s__' % method for method in [
+ 'cmp', 'getslice', 'setslice', 'coerce', 'subclasses',
+ 'format', 'get', 'set', 'delete', 'reversed',
+ 'missing', 'reduce', 'reduce_ex', 'getinitargs',
+ 'getnewargs', 'getstate', 'setstate', 'getformat',
+ 'setformat', 'repr', 'dir'
+])
+
+
+def _get_method(name, func):
+ "Turns a callable object (like a mock) into a real function"
+ def method(self, *args, **kw):
+ return func(self, *args, **kw)
+ method.__name__ = name
+ return method
+
+
+_magics = set(
+ '__%s__' % method for method in
+ ' '.join([magic_methods, numerics, inplace, right, extra]).split()
+)
+
+_all_magics = _magics | _non_defaults
+
+_unsupported_magics = set([
+ '__getattr__', '__setattr__',
+ '__init__', '__new__', '__prepare__'
+ '__instancecheck__', '__subclasscheck__',
+ '__del__'
+])
+
+_calculate_return_value = {
+ '__hash__': lambda self: object.__hash__(self),
+ '__str__': lambda self: object.__str__(self),
+ '__sizeof__': lambda self: object.__sizeof__(self),
+ '__unicode__': lambda self: unicode(object.__str__(self)),
+}
+
+_return_values = {
+ '__int__': 1,
+ '__contains__': False,
+ '__len__': 0,
+ '__exit__': False,
+ '__complex__': 1j,
+ '__float__': 1.0,
+ '__bool__': True,
+ '__nonzero__': True,
+ '__oct__': '1',
+ '__hex__': '0x1',
+ '__long__': long(1),
+ '__index__': 1,
+}
+
+
+def _get_eq(self):
+ def __eq__(other):
+ ret_val = self.__eq__._mock_return_value
+ if ret_val is not DEFAULT:
+ return ret_val
+ return self is other
+ return __eq__
+
+def _get_ne(self):
+ def __ne__(other):
+ if self.__ne__._mock_return_value is not DEFAULT:
+ return DEFAULT
+ return self is not other
+ return __ne__
+
+def _get_iter(self):
+ def __iter__():
+ ret_val = self.__iter__._mock_return_value
+ if ret_val is DEFAULT:
+ return iter([])
+ # if ret_val was already an iterator, then calling iter on it should
+ # return the iterator unchanged
+ return iter(ret_val)
+ return __iter__
+
+_side_effect_methods = {
+ '__eq__': _get_eq,
+ '__ne__': _get_ne,
+ '__iter__': _get_iter,
+}
+
+
+
+def _set_return_value(mock, method, name):
+ fixed = _return_values.get(name, DEFAULT)
+ if fixed is not DEFAULT:
+ method.return_value = fixed
+ return
+
+ return_calulator = _calculate_return_value.get(name)
+ if return_calulator is not None:
+ try:
+ return_value = return_calulator(mock)
+ except AttributeError:
+ # XXXX why do we return AttributeError here?
+ # set it as a side_effect instead?
+ return_value = AttributeError(name)
+ method.return_value = return_value
+ return
+
+ side_effector = _side_effect_methods.get(name)
+ if side_effector is not None:
+ method.side_effect = side_effector(mock)
+
+
+
+class MagicMixin(object):
+ def __init__(self, *args, **kw):
+ _super(MagicMixin, self).__init__(*args, **kw)
+ self._mock_set_magics()
+
+
+ def _mock_set_magics(self):
+ these_magics = _magics
+
+ if self._mock_methods is not None:
+ these_magics = _magics.intersection(self._mock_methods)
+
+ remove_magics = set()
+ remove_magics = _magics - these_magics
+
+ for entry in remove_magics:
+ if entry in type(self).__dict__:
+ # remove unneeded magic methods
+ delattr(self, entry)
+
+ # don't overwrite existing attributes if called a second time
+ these_magics = these_magics - set(type(self).__dict__)
+
+ _type = type(self)
+ for entry in these_magics:
+ setattr(_type, entry, MagicProxy(entry, self))
+
+
+
+class NonCallableMagicMock(MagicMixin, NonCallableMock):
+ """A version of `MagicMock` that isn't callable."""
+ def mock_add_spec(self, spec, spec_set=False):
+ """Add a spec to a mock. `spec` can either be an object or a
+ list of strings. Only attributes on the `spec` can be fetched as
+ attributes from the mock.
+
+ If `spec_set` is True then only attributes on the spec can be set."""
+ self._mock_add_spec(spec, spec_set)
+ self._mock_set_magics()
+
+
+
+class MagicMock(MagicMixin, Mock):
+ """
+ MagicMock is a subclass of Mock with default implementations
+ of most of the magic methods. You can use MagicMock without having to
+ configure the magic methods yourself.
+
+ If you use the `spec` or `spec_set` arguments then *only* magic
+ methods that exist in the spec will be created.
+
+ Attributes and the return value of a `MagicMock` will also be `MagicMocks`.
+ """
+ def mock_add_spec(self, spec, spec_set=False):
+ """Add a spec to a mock. `spec` can either be an object or a
+ list of strings. Only attributes on the `spec` can be fetched as
+ attributes from the mock.
+
+ If `spec_set` is True then only attributes on the spec can be set."""
+ self._mock_add_spec(spec, spec_set)
+ self._mock_set_magics()
+
+
+
+class MagicProxy(object):
+ def __init__(self, name, parent):
+ self.name = name
+ self.parent = parent
+
+ def __call__(self, *args, **kwargs):
+ m = self.create_mock()
+ return m(*args, **kwargs)
+
+ def create_mock(self):
+ entry = self.name
+ parent = self.parent
+ m = parent._get_child_mock(name=entry, _new_name=entry,
+ _new_parent=parent)
+ setattr(parent, entry, m)
+ _set_return_value(parent, m, entry)
+ return m
+
+ def __get__(self, obj, _type=None):
+ return self.create_mock()
+
+
+
+class _ANY(object):
+ "A helper object that compares equal to everything."
+
+ def __eq__(self, other):
+ return True
+
+ def __ne__(self, other):
+ return False
+
+ def __repr__(self):
+ return ''
+
+ANY = _ANY()
+
+
+
+def _format_call_signature(name, args, kwargs):
+ message = '%s(%%s)' % name
+ formatted_args = ''
+ args_string = ', '.join([repr(arg) for arg in args])
+ kwargs_string = ', '.join([
+ '%s=%r' % (key, value) for key, value in kwargs.items()
+ ])
+ if args_string:
+ formatted_args = args_string
+ if kwargs_string:
+ if formatted_args:
+ formatted_args += ', '
+ formatted_args += kwargs_string
+
+ return message % formatted_args
+
+
+
+class _Call(tuple):
+ """
+ A tuple for holding the results of a call to a mock, either in the form
+ `(args, kwargs)` or `(name, args, kwargs)`.
+
+ If args or kwargs are empty then a call tuple will compare equal to
+ a tuple without those values. This makes comparisons less verbose::
+
+ _Call(('name', (), {})) == ('name',)
+ _Call(('name', (1,), {})) == ('name', (1,))
+ _Call(((), {'a': 'b'})) == ({'a': 'b'},)
+
+ The `_Call` object provides a useful shortcut for comparing with call::
+
+ _Call(((1, 2), {'a': 3})) == call(1, 2, a=3)
+ _Call(('foo', (1, 2), {'a': 3})) == call.foo(1, 2, a=3)
+
+ If the _Call has no name then it will match any name.
+ """
+ def __new__(cls, value=(), name=None, parent=None, two=False,
+ from_kall=True):
+ name = ''
+ args = ()
+ kwargs = {}
+ _len = len(value)
+ if _len == 3:
+ name, args, kwargs = value
+ elif _len == 2:
+ first, second = value
+ if isinstance(first, basestring):
+ name = first
+ if isinstance(second, tuple):
+ args = second
+ else:
+ kwargs = second
+ else:
+ args, kwargs = first, second
+ elif _len == 1:
+ value, = value
+ if isinstance(value, basestring):
+ name = value
+ elif isinstance(value, tuple):
+ args = value
+ else:
+ kwargs = value
+
+ if two:
+ return tuple.__new__(cls, (args, kwargs))
+
+ return tuple.__new__(cls, (name, args, kwargs))
+
+
+ def __init__(self, value=(), name=None, parent=None, two=False,
+ from_kall=True):
+ self.name = name
+ self.parent = parent
+ self.from_kall = from_kall
+
+
+ def __eq__(self, other):
+ if other is ANY:
+ return True
+ try:
+ len_other = len(other)
+ except TypeError:
+ return False
+
+ self_name = ''
+ if len(self) == 2:
+ self_args, self_kwargs = self
+ else:
+ self_name, self_args, self_kwargs = self
+
+ other_name = ''
+ if len_other == 0:
+ other_args, other_kwargs = (), {}
+ elif len_other == 3:
+ other_name, other_args, other_kwargs = other
+ elif len_other == 1:
+ value, = other
+ if isinstance(value, tuple):
+ other_args = value
+ other_kwargs = {}
+ elif isinstance(value, basestring):
+ other_name = value
+ other_args, other_kwargs = (), {}
+ else:
+ other_args = ()
+ other_kwargs = value
+ else:
+ # len 2
+ # could be (name, args) or (name, kwargs) or (args, kwargs)
+ first, second = other
+ if isinstance(first, basestring):
+ other_name = first
+ if isinstance(second, tuple):
+ other_args, other_kwargs = second, {}
+ else:
+ other_args, other_kwargs = (), second
+ else:
+ other_args, other_kwargs = first, second
+
+ if self_name and other_name != self_name:
+ return False
+
+ # this order is important for ANY to work!
+ return (other_args, other_kwargs) == (self_args, self_kwargs)
+
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+
+ def __call__(self, *args, **kwargs):
+ if self.name is None:
+ return _Call(('', args, kwargs), name='()')
+
+ name = self.name + '()'
+ return _Call((self.name, args, kwargs), name=name, parent=self)
+
+
+ def __getattr__(self, attr):
+ if self.name is None:
+ return _Call(name=attr, from_kall=False)
+ name = '%s.%s' % (self.name, attr)
+ return _Call(name=name, parent=self, from_kall=False)
+
+
+ def __repr__(self):
+ if not self.from_kall:
+ name = self.name or 'call'
+ if name.startswith('()'):
+ name = 'call%s' % name
+ return name
+
+ if len(self) == 2:
+ name = 'call'
+ args, kwargs = self
+ else:
+ name, args, kwargs = self
+ if not name:
+ name = 'call'
+ elif not name.startswith('()'):
+ name = 'call.%s' % name
+ else:
+ name = 'call%s' % name
+ return _format_call_signature(name, args, kwargs)
+
+
+ def call_list(self):
+ """For a call object that represents multiple calls, `call_list`
+ returns a list of all the intermediate calls as well as the
+ final call."""
+ vals = []
+ thing = self
+ while thing is not None:
+ if thing.from_kall:
+ vals.append(thing)
+ thing = thing.parent
+ return _CallList(reversed(vals))
+
+
+call = _Call(from_kall=False)
+
+
+
+def create_autospec(spec, spec_set=False, instance=False, _parent=None,
+ _name=None, **kwargs):
+ """Create a mock object using another object as a spec. Attributes on the
+ mock will use the corresponding attribute on the `spec` object as their
+ spec.
+
+ Functions or methods being mocked will have their arguments checked in a
+ similar way to `mocksignature` to check that they are called with the
+ correct signature.
+
+ If `spec_set` is True then attempting to set attributes that don't exist
+ on the spec object will raise an `AttributeError`.
+
+ If a class is used as a spec then the return value of the mock (the
+ instance of the class) will have the same spec. You can use a class as the
+ spec for an instance object by passing `instance=True`. The returned mock
+ will only be callable if instances of the mock are callable.
+
+ `create_autospec` also takes arbitrary keyword arguments that are passed to
+ the constructor of the created mock."""
+ if _is_list(spec):
+ # can't pass a list instance to the mock constructor as it will be
+ # interpreted as a list of strings
+ spec = type(spec)
+
+ is_type = isinstance(spec, ClassTypes)
+
+ _kwargs = {'spec': spec}
+ if spec_set:
+ _kwargs = {'spec_set': spec}
+ elif spec is None:
+ # None we mock with a normal mock without a spec
+ _kwargs = {}
+
+ _kwargs.update(kwargs)
+
+ Klass = MagicMock
+ if type(spec) in DescriptorTypes:
+ # descriptors don't have a spec
+ # because we don't know what type they return
+ _kwargs = {}
+ elif not _callable(spec):
+ Klass = NonCallableMagicMock
+ elif is_type and instance and not _instance_callable(spec):
+ Klass = NonCallableMagicMock
+
+ _new_name = _name
+ if _parent is None:
+ # for a top level object no _new_name should be set
+ _new_name = ''
+
+ mock = Klass(parent=_parent, _new_parent=_parent, _new_name=_new_name,
+ name=_name, **_kwargs)
+
+ if isinstance(spec, FunctionTypes):
+ # should only happen at the top level because we don't
+ # recurse for functions
+ mock = _set_signature(mock, spec)
+ else:
+ _check_signature(spec, mock, is_type, instance)
+
+ if _parent is not None and not instance:
+ _parent._mock_children[_name] = mock
+
+ if is_type and not instance and 'return_value' not in kwargs:
+ # XXXX could give a name to the return_value mock?
+ mock.return_value = create_autospec(spec, spec_set, instance=True,
+ _name='()', _parent=mock)
+
+ for entry in dir(spec):
+ if _is_magic(entry):
+ # MagicMock already does the useful magic methods for us
+ continue
+
+ if isinstance(spec, FunctionTypes) and entry in FunctionAttributes:
+ # allow a mock to actually be a function from mocksignature
+ continue
+
+ # XXXX do we need a better way of getting attributes without
+ # triggering code execution (?) Probably not - we need the actual
+ # object to mock it so we would rather trigger a property than mock
+ # the property descriptor. Likewise we want to mock out dynamically
+ # provided attributes.
+ # XXXX what about attributes that raise exceptions on being fetched
+ # we could be resilient against it, or catch and propagate the
+ # exception when the attribute is fetched from the mock
+ original = getattr(spec, entry)
+
+ kwargs = {'spec': original}
+ if spec_set:
+ kwargs = {'spec_set': original}
+
+ if not isinstance(original, FunctionTypes):
+ new = _SpecState(original, spec_set, mock, entry, instance)
+ mock._mock_children[entry] = new
+ else:
+ parent = mock
+ if isinstance(spec, FunctionTypes):
+ parent = mock.mock
+
+ new = MagicMock(parent=parent, name=entry, _new_name=entry,
+ _new_parent=parent, **kwargs)
+ mock._mock_children[entry] = new
+ skipfirst = _must_skip(spec, entry, is_type)
+ _check_signature(original, new, skipfirst=skipfirst)
+
+ # so functions created with mocksignature become instance attributes,
+ # *plus* their underlying mock exists in _mock_children of the parent
+ # mock. Adding to _mock_children may be unnecessary where we are also
+ # setting as an instance attribute?
+ if isinstance(new, FunctionTypes):
+ setattr(mock, entry, new)
+
+ return mock
+
+
+def _must_skip(spec, entry, is_type):
+ if not isinstance(spec, ClassTypes):
+ if entry in getattr(spec, '__dict__', {}):
+ # instance attribute - shouldn't skip
+ return False
+ # can't use type because of old style classes
+ spec = spec.__class__
+ if not hasattr(spec, '__mro__'):
+ # old style class: can't have descriptors anyway
+ return is_type
+
+ for klass in spec.__mro__:
+ result = klass.__dict__.get(entry, DEFAULT)
+ if result is DEFAULT:
+ continue
+ if isinstance(result, (staticmethod, classmethod)):
+ return False
+ return is_type
+
+ # shouldn't get here unless function is a dynamically provided attribute
+ # XXXX untested behaviour
+ return is_type
+
+
+def _get_class(obj):
+ try:
+ return obj.__class__
+ except AttributeError:
+ # in Python 2, _sre.SRE_Pattern objects have no __class__
+ return type(obj)
+
+
+class _SpecState(object):
+
+ def __init__(self, spec, spec_set=False, parent=None,
+ name=None, ids=None, instance=False):
+ self.spec = spec
+ self.ids = ids
+ self.spec_set = spec_set
+ self.parent = parent
+ self.instance = instance
+ self.name = name
+
+
+FunctionTypes = (
+ # python function
+ type(create_autospec),
+ # instance method
+ type(ANY.__eq__),
+ # unbound method
+ type(_ANY.__eq__),
+)
+
+FunctionAttributes = set([
+ 'func_closure',
+ 'func_code',
+ 'func_defaults',
+ 'func_dict',
+ 'func_doc',
+ 'func_globals',
+ 'func_name',
+])
diff --git a/script/__Lib/diffcalc-2.1/model/README.txt b/script/__Lib/diffcalc-2.1/model/README.txt
new file mode 100755
index 0000000..45d8fcb
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/model/README.txt
@@ -0,0 +1,13 @@
+Setting up the sixc vrml model to work via Epics pv's from b16's simulation running on the office network.
+
+
+1. Synoptic
+$ launcher --port 6064
+
+2. Shell
+export EPICS_CA_REPEATER_PORT=6065
+export EPICS_CA_SERVER_PORT=6064
+
+3. Vrml viewer
+export EPICS_CA_SERVER_PORT=6064
+dls-vrml-epics-viewer.py fivec.wrl fivec.wcfg
diff --git a/script/__Lib/diffcalc-2.1/model/fivec.fxw b/script/__Lib/diffcalc-2.1/model/fivec.fxw
new file mode 100755
index 0000000..de9ae05
Binary files /dev/null and b/script/__Lib/diffcalc-2.1/model/fivec.fxw differ
diff --git a/script/__Lib/diffcalc-2.1/model/fivec.wcfg b/script/__Lib/diffcalc-2.1/model/fivec.wcfg
new file mode 100755
index 0000000..8f96e1e
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/model/fivec.wcfg
@@ -0,0 +1,5 @@
+dad_alpha_frame rotation pv["DIFFSIM:FIVEC:ALPHA.RBV"]
+dad_delta_frame rotation pv["DIFFSIM:FIVEC:DELTA.RBV"]
+dad_omega_frame rotation pv["DIFFSIM:FIVEC:OMEGA.RBV"]
+dad_chi_frame rotation pv["DIFFSIM:FIVEC:CHI.RBV"]
+dad_phi_frame rotation pv["DIFFSIM:FIVEC:PHI.RBV"]
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/model/fivec.wrl b/script/__Lib/diffcalc-2.1/model/fivec.wrl
new file mode 100755
index 0000000..85f6fad
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/model/fivec.wrl
@@ -0,0 +1,1011 @@
+#VRML V2.0 utf8
+WorldInfo {
+ title "fourc"
+ info ["This Web3D Content was created with Flux Studio, a Web3D authoring tool"
+ "www.mediamachines.com"
+ "This Web3D Content was created with Flux Studio, a Web3D authoring tool"]
+}
+## Vizthumbnail Thumb_fivec_wrl5688291255594457.jpg
+DirectionalLight {
+ intensity 1.000
+ ambientIntensity 0.500
+ direction -.621 -.23944 -.74634
+ color 1 1 1
+ on TRUE
+}
+DirectionalLight {
+ intensity 1.000
+ ambientIntensity 0.000
+ direction -.01373 -.46642 -.88446
+ color 1 1 1
+ on TRUE
+}
+DEF lab_x Shape {
+ appearance Appearance {
+ material DEF Rust Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .50196 .50196 .50196
+ }
+ }
+ geometry DEF GeoCylinder15 Cylinder {
+ height 60.000
+ radius 0.020
+ }
+}
+DEF dad_lab_z Transform {
+ rotation 1 0 0 1.571
+ children [
+ DEF lab_z Shape {
+ appearance Appearance {
+ material USE Rust
+ }
+ geometry DEF GeoCylinder17 Cylinder {
+ height 40.000
+ radius 0.020
+ }
+ }
+ ]
+}
+DEF dad_lab_beam Transform {
+ translation 10 0 0
+ rotation 0 0 1 1.571
+ children [
+ DEF lab_beam Shape {
+ appearance Appearance {
+ material DEF White Material {
+ ambientIntensity 0.500
+ shininess 0.100
+ transparency 0.500
+ diffuseColor 1 1 1
+ emissiveColor 1 1 1
+ }
+ }
+ geometry DEF GeoCylinder20 Cylinder {
+ height 20.000
+ radius 0.050
+ }
+ }
+ ]
+}
+DEF dad_lab_beam0 Transform {
+ translation 20 0 0
+ rotation 0 0 1 1.571
+ children [
+ DEF lab_beam0 Shape {
+ appearance Appearance {
+ material DEF Shiny_Black Material {
+ ambientIntensity 0.200
+ shininess 0.100
+ diffuseColor 1 1 1
+ }
+ }
+ geometry DEF GeoCylinder22 Cylinder {
+ height 4.000
+ radius 0.200
+ }
+ }
+ ]
+}
+DEF dad_Cone2 Transform {
+ translation 17.5 0 0
+ rotation 0 0 1 1.571
+ children [
+ DEF Cone2 Shape {
+ appearance Appearance {
+ material USE Shiny_Black
+ }
+ geometry DEF GeoCone2 Cone {
+ height 1.500
+ bottomRadius 0.500
+ }
+ }
+ ]
+}
+DEF dad_lab_y Transform {
+ rotation 0 0 -1 1.571
+ children [
+ DEF lab_y Shape {
+ appearance Appearance {
+ material USE Rust
+ }
+ geometry DEF GeoCylinder16 Cylinder {
+ height 60.000
+ radius 0.020
+ }
+ }
+ ]
+}
+DEF dad_alpha_frame Transform {
+ rotation 0 1 0 +.349
+ children [
+ DEF dad_alpha_base Transform {
+ translation 0 -20.5 0
+ children [
+ DEF alpha_base Shape {
+ appearance Appearance {
+ material DEF Red Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .50196 0 0
+ }
+ }
+ geometry DEF GeoCylinder1 Cylinder {
+ height 2.000
+ radius 6.000
+ }
+ }
+ ]
+ }
+ DEF dad_lab_post Transform {
+ translation 0 -10 -11
+ children [
+ DEF alpha_post Shape {
+ appearance Appearance {
+ material USE Red
+ }
+ geometry DEF GeoBox1 Box {
+ size 4 20 1
+ }
+ }
+ ]
+ }
+ DEF dad_omega_frame Transform {
+ rotation 0 0 1 -.349
+ children [
+ DEF dad_chi_frame Transform {
+ rotation 1 0 0 -.785
+ children [
+ DEF dad_phi_frame Transform {
+ rotation 0 0 1 -.349
+ children [
+ DEF dad_Cylinder7 Transform {
+ translation 0 0 -4
+ rotation 1 0 0 1.571
+ children [
+ DEF Cylinder7 Shape {
+ appearance Appearance {
+ material USE White
+ }
+ geometry DEF GeoCylinder7 Cylinder {
+ height 2.000
+ radius 1.000
+ }
+ }
+ ]
+ }
+ DEF dad_Cylinder8 Transform {
+ translation 0 0 -3.5
+ rotation 1 0 0 1.571
+ children [
+ DEF Cylinder8 Shape {
+ appearance Appearance {
+ material USE White
+ }
+ geometry DEF GeoCylinder8 Cylinder {
+ height 6.000
+ radius 0.250
+ }
+ }
+ ]
+ }
+ DEF Box3 Shape {
+ appearance Appearance {
+ material USE White
+ }
+ geometry DEF GeoBox3 Box {
+ size 1 1 1
+ }
+ }
+ DEF dad_Box11 Transform {
+ translation 0 1 -4
+ children [
+ DEF Box11 Shape {
+ appearance Appearance {
+ material USE White
+ }
+ geometry DEF GeoBox11 Box {
+ size .25 .5 2
+ }
+ }
+ ]
+ }
+ DEF Box4 Shape {
+ appearance Appearance {
+ material DEF White_wire Material {
+ emissiveColor 1 1 1
+ }
+ }
+ geometry IndexedLineSet {
+ coordIndex [
+ 0 1 2 0
+ -1 0 2 3
+ 0 -1 1 5
+ 6 1 -1 1
+ 6 2 1 -1
+ 2 6 7 2
+ -1 2 7 3
+ 2 -1 3 7
+ 4 3 -1 3
+ 4 0 3 -1
+ 0 4 5 0
+ -1 0 5 1
+ 0 -1 6 5
+ 4 6 -1 6
+ 4 7 6 -1
+ ]
+ coord Coordinate {
+ point [
+ -.5 .5 -.5
+ -.5 .5 .5
+ .5 .5 .5
+ .5 .5 -.5
+ -.5 -.5 -.5
+ -.5 -.5 .5
+ .5 -.5 .5
+ .5 -.5 -.5
+ ]
+ }
+ }
+ }
+ ]
+ }
+ DEF dad_Box2 Transform {
+ translation 0 0 -5.5
+ children [
+ DEF Box2 Shape {
+ appearance Appearance {
+ material DEF Purple Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .9176 0 .874
+ }
+ }
+ geometry DEF GeoBox2 Box {
+ size 2.8 2.8 1.5
+ }
+ }
+ ]
+ }
+ DEF dad_Box6 Transform {
+ translation 0 1.25 -5.5
+ children [
+ DEF Box6 Shape {
+ appearance Appearance {
+ material USE Purple
+ }
+ geometry DEF GeoBox21 Box {
+ size .25 1 1.5
+ }
+ }
+ ]
+ }
+ ]
+ }
+ DEF dad_Cylinder2 Transform {
+ rotation 0 0 1 1.571
+ scale 1.1 1.5 1.1
+ children [
+ DEF Cylinder2 Shape {
+ appearance Appearance {
+ material DEF Green Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .00784 .65098 .02353
+ }
+ }
+ geometry DEF Cylinder2_Geo IndexedFaceSet {
+ creaseAngle 0.524
+ coordIndex [
+ 49 50 48 -1
+ 48 50 51 -1
+ 48 51 47 -1
+ 47 51 20 -1
+ 47 20 21 -1
+ 12 13 56 -1
+ 56 13 14 -1
+ 56 14 55 -1
+ 55 14 15 -1
+ 55 15 54 -1
+ 54 15 16 -1
+ 54 16 53 -1
+ 53 16 17 -1
+ 53 17 52 -1
+ 52 17 18 -1
+ 52 18 51 -1
+ 51 18 19 -1
+ 51 19 20 -1
+ 5 6 62 -1
+ 62 6 7 -1
+ 62 7 61 -1
+ 61 7 8 -1
+ 61 8 60 -1
+ 60 8 9 -1
+ 60 9 59 -1
+ 59 9 10 -1
+ 59 10 58 -1
+ 58 10 11 -1
+ 58 11 57 -1
+ 57 11 12 -1
+ 57 12 56 -1
+ 5 63 4 -1
+ 4 63 64 -1
+ 4 64 3 -1
+ 3 64 1 -1
+ 3 1 2 -1
+ 2 1 35 -1
+ 2 35 36 -1
+ 28 41 27 -1
+ 27 41 42 -1
+ 27 42 26 -1
+ 26 42 43 -1
+ 26 43 25 -1
+ 25 43 44 -1
+ 25 44 24 -1
+ 24 44 45 -1
+ 24 45 23 -1
+ 23 45 46 -1
+ 23 46 22 -1
+ 22 46 47 -1
+ 22 47 21 -1
+ 34 36 33 -1
+ 33 36 37 -1
+ 33 37 32 -1
+ 32 37 38 -1
+ 32 38 31 -1
+ 31 38 39 -1
+ 31 39 30 -1
+ 30 39 40 -1
+ 30 40 29 -1
+ 29 40 41 -1
+ 29 41 28 -1
+ 1 64 0 -1
+ 2 36 34 -1
+ 63 5 62 -1
+ 95 96 97 -1
+ 97 96 65 -1
+ 97 65 129 -1
+ 129 65 66 -1
+ 129 66 128 -1
+ 128 66 67 -1
+ 128 67 127 -1
+ 127 67 126 -1
+ 104 105 88 -1
+ 88 105 106 -1
+ 88 106 87 -1
+ 87 106 107 -1
+ 87 107 86 -1
+ 86 107 108 -1
+ 86 108 85 -1
+ 85 108 109 -1
+ 85 109 84 -1
+ 84 109 110 -1
+ 84 110 83 -1
+ 83 110 111 -1
+ 83 111 82 -1
+ 82 111 112 -1
+ 82 112 113 -1
+ 98 99 93 -1
+ 93 99 100 -1
+ 93 100 92 -1
+ 92 100 101 -1
+ 92 101 91 -1
+ 91 101 102 -1
+ 91 102 90 -1
+ 90 102 103 -1
+ 90 103 89 -1
+ 89 103 104 -1
+ 89 104 88 -1
+ 94 95 97 -1
+ 94 97 98 -1
+ 94 98 93 -1
+ 81 82 78 -1
+ 81 78 79 -1
+ 81 79 80 -1
+ 78 82 113 -1
+ 78 113 114 -1
+ 78 114 115 -1
+ 78 115 77 -1
+ 120 73 119 -1
+ 119 73 74 -1
+ 119 74 118 -1
+ 118 74 75 -1
+ 118 75 117 -1
+ 117 75 76 -1
+ 117 76 116 -1
+ 116 76 77 -1
+ 116 77 115 -1
+ 126 68 125 -1
+ 125 68 69 -1
+ 125 69 124 -1
+ 124 69 70 -1
+ 124 70 123 -1
+ 123 70 71 -1
+ 123 71 122 -1
+ 122 71 72 -1
+ 122 72 121 -1
+ 121 72 73 -1
+ 121 73 120 -1
+ 126 67 68 -1
+ 0 64 65 -1
+ 0 65 96 -1
+ 64 63 66 -1
+ 64 66 65 -1
+ 63 62 67 -1
+ 63 67 66 -1
+ 62 61 68 -1
+ 62 68 67 -1
+ 61 60 69 -1
+ 61 69 68 -1
+ 60 59 70 -1
+ 60 70 69 -1
+ 59 58 71 -1
+ 59 71 70 -1
+ 58 57 72 -1
+ 58 72 71 -1
+ 57 56 73 -1
+ 57 73 72 -1
+ 56 55 74 -1
+ 56 74 73 -1
+ 55 54 75 -1
+ 55 75 74 -1
+ 54 53 76 -1
+ 54 76 75 -1
+ 53 52 77 -1
+ 53 77 76 -1
+ 52 51 78 -1
+ 52 78 77 -1
+ 51 50 79 -1
+ 51 79 78 -1
+ 50 49 80 -1
+ 50 80 79 -1
+ 49 48 81 -1
+ 49 81 80 -1
+ 48 47 82 -1
+ 48 82 81 -1
+ 47 46 83 -1
+ 47 83 82 -1
+ 46 45 84 -1
+ 46 84 83 -1
+ 45 44 85 -1
+ 45 85 84 -1
+ 44 43 86 -1
+ 44 86 85 -1
+ 43 42 87 -1
+ 43 87 86 -1
+ 42 41 88 -1
+ 42 88 87 -1
+ 41 40 89 -1
+ 41 89 88 -1
+ 40 39 90 -1
+ 40 90 89 -1
+ 39 38 91 -1
+ 39 91 90 -1
+ 38 37 92 -1
+ 38 92 91 -1
+ 37 36 93 -1
+ 37 93 92 -1
+ 36 35 94 -1
+ 36 94 93 -1
+ 35 1 95 -1
+ 35 95 94 -1
+ 1 0 96 -1
+ 1 96 95 -1
+ ]
+ coord DEF Cylinder2_Coord Coordinate {
+ point [
+ 0 1 -6
+ -1.17054 1 -5.88471
+ -1.07212 1 -5.3899
+ -1.05189 1 -5.39596
+ .02145 1 -5.49809
+ 1.09408 1 -5.38886
+ 2.1248 1 -5.07248
+ 3.07398 1 -4.56108
+ 3.90517 1 -3.87434
+ 4.5864 1 -3.03864
+ 5.0915 1 -2.08609
+ 5.40107 1 -1.0533
+ 5.5032 1 .02003
+ 5.39398 1 1.09267
+ 5.07759 1 2.12338
+ 4.5662 1 3.07257
+ 3.87945 1 3.90375
+ 3.04375 1 4.58498
+ 2.0912 1 5.09009
+ 1.05842 1 5.39966
+ -.01492 1 5.50179
+ -1.08755 1 5.39256
+ -2.11827 1 5.07617
+ -3.06746 1 4.56478
+ -3.89864 1 3.87804
+ -4.57987 1 3.04233
+ -5.08498 1 2.08979
+ -5.39454 1 1.057
+ -5.49668 1 -.01633
+ -5.38745 1 -1.08897
+ -5.07106 1 -2.11968
+ -4.55967 1 -3.06887
+ -3.87292 1 -3.90005
+ -3.03722 1 -4.58129
+ -2.08467 1 -5.08639
+ -2.2961 1 -5.54328
+ -3.33342 1 -4.98882
+ -4.24264 1 -4.24264
+ -4.98882 1 -3.33342
+ -5.54328 1 -2.2961
+ -5.88471 1 -1.17054
+ -6 1 -0
+ -5.88471 1 1.17054
+ -5.54328 1 2.2961
+ -4.98882 1 3.33342
+ -4.24264 1 4.24264
+ -3.33342 1 4.98882
+ -2.2961 1 5.54328
+ -1.17054 1 5.88471
+ -0 1 6
+ 1.17054 1 5.88471
+ 2.2961 1 5.54328
+ 3.33342 1 4.98882
+ 4.24264 1 4.24264
+ 4.98882 1 3.33342
+ 5.54328 1 2.2961
+ 5.88471 1 1.17054
+ 6 1 0
+ 5.88471 1 -1.17054
+ 5.54328 1 -2.2961
+ 4.98882 1 -3.33342
+ 4.24264 1 -4.24264
+ 3.33342 1 -4.98882
+ 2.2961 1 -5.54328
+ 1.17054 1 -5.88471
+ 1.17054 -1 -5.88471
+ 2.2961 -1 -5.54328
+ 3.33342 -1 -4.98882
+ 4.24264 -1 -4.24264
+ 4.98882 -1 -3.33342
+ 5.54328 -1 -2.2961
+ 5.88471 -1 -1.17054
+ 6 -1 -0
+ 5.88471 -1 1.17054
+ 5.54328 -1 2.2961
+ 4.98882 -1 3.33342
+ 4.24264 -1 4.24264
+ 3.33342 -1 4.98882
+ 2.2961 -1 5.54328
+ 1.17054 -1 5.88471
+ -0 -1 6
+ -1.17054 -1 5.88471
+ -2.2961 -1 5.54328
+ -3.33342 -1 4.98882
+ -4.24264 -1 4.24264
+ -4.98882 -1 3.33342
+ -5.54328 -1 2.2961
+ -5.88471 -1 1.17054
+ -6 -1 -0
+ -5.88471 -1 -1.17054
+ -5.54328 -1 -2.2961
+ -4.98882 -1 -3.33342
+ -4.24264 -1 -4.24264
+ -3.33342 -1 -4.98882
+ -2.2961 -1 -5.54328
+ -1.17054 -1 -5.88471
+ 0 -1 -6
+ 0 -1 -5.50037
+ -1.05842 -1 -5.39966
+ -2.0912 -1 -5.09009
+ -3.04375 -1 -4.58498
+ -3.87945 -1 -3.90375
+ -4.5662 -1 -3.07257
+ -5.07759 -1 -2.12338
+ -5.39398 -1 -1.09267
+ -5.5032 -1 -.02003
+ -5.40107 -1 1.0533
+ -5.0915 -1 2.08609
+ -4.5864 -1 3.03864
+ -3.90517 -1 3.87434
+ -3.07398 -1 4.56108
+ -2.1248 -1 5.07248
+ -1.09408 -1 5.38886
+ -.02145 -1 5.49809
+ 1.05189 -1 5.39596
+ 2.08467 -1 5.08639
+ 3.03722 -1 4.58129
+ 3.87292 -1 3.90005
+ 4.55967 -1 3.06887
+ 5.07106 -1 2.11968
+ 5.38745 -1 1.08897
+ 5.49668 -1 .01633
+ 5.39454 -1 -1.057
+ 5.08498 -1 -2.08979
+ 4.57987 -1 -3.04233
+ 3.89864 -1 -3.87804
+ 3.06746 -1 -4.56478
+ 2.11827 -1 -5.07617
+ 1.08755 -1 -5.39256
+ .01492 -1 -5.50179
+ ]
+ }
+ }
+ }
+ ]
+ }
+ DEF dad_Cylinder3 Transform {
+ rotation 0 0 1 1.571
+ scale 1.1 1.5 1.1
+ children [
+ DEF Cylinder3 Shape {
+ appearance Appearance {
+ material USE Green
+ }
+ geometry DEF Cylinder3_Geo IndexedFaceSet {
+ creaseAngle 0.524
+ coordIndex [
+ 0 1 2 -1
+ 0 2 3 -1
+ 4 5 1 -1
+ 4 1 0 -1
+ 6 7 5 -1
+ 6 5 4 -1
+ 8 9 7 -1
+ 8 7 6 -1
+ 10 11 9 -1
+ 10 9 8 -1
+ 12 13 11 -1
+ 12 11 10 -1
+ 14 15 13 -1
+ 14 13 12 -1
+ 16 17 15 -1
+ 16 15 14 -1
+ 18 19 17 -1
+ 18 17 16 -1
+ 20 21 19 -1
+ 20 19 18 -1
+ 22 23 21 -1
+ 22 21 20 -1
+ 24 25 23 -1
+ 24 23 22 -1
+ 26 27 25 -1
+ 26 25 24 -1
+ 28 29 27 -1
+ 28 27 26 -1
+ 30 31 29 -1
+ 30 29 28 -1
+ 32 33 31 -1
+ 32 31 30 -1
+ 34 35 33 -1
+ 34 33 32 -1
+ 36 37 35 -1
+ 36 35 34 -1
+ 38 39 37 -1
+ 38 37 36 -1
+ 40 41 39 -1
+ 40 39 38 -1
+ 42 43 41 -1
+ 42 41 40 -1
+ 44 45 43 -1
+ 44 43 42 -1
+ 46 47 45 -1
+ 46 45 44 -1
+ 48 49 47 -1
+ 48 47 46 -1
+ 50 51 49 -1
+ 50 49 48 -1
+ 52 53 51 -1
+ 52 51 50 -1
+ 54 55 53 -1
+ 54 53 52 -1
+ 56 57 55 -1
+ 56 55 54 -1
+ 58 59 57 -1
+ 58 57 56 -1
+ 60 61 59 -1
+ 60 59 58 -1
+ 62 63 61 -1
+ 62 61 60 -1
+ 3 2 63 -1
+ 3 63 62 -1
+ ]
+ coord DEF Cylinder3_Coord Coordinate {
+ point [
+ 1.073 -1.00635 -5.39432
+ 1.073 .99367 -5.39432
+ 0 .98997 -5.5
+ 0 -1.01006 -5.5
+ 2.10476 -1.0024 -5.08134
+ 2.10476 .99763 -5.08134
+ 3.05564 -.99836 -4.57308
+ 3.05564 1.00167 -4.57308
+ 3.88909 -.99438 -3.88909
+ 3.88909 1.00565 -3.88909
+ 4.57308 -.99061 -3.05564
+ 4.57308 1.00941 -3.05564
+ 5.08134 -.98721 -2.10476
+ 5.08134 1.01282 -2.10476
+ 5.39432 -.9843 -1.073
+ 5.39432 1.01573 -1.073
+ 5.5 -.98199 -0
+ 5.5 1.01803 -0
+ 5.39432 -.98038 1.073
+ 5.39432 1.01965 1.073
+ 5.08134 -.97952 2.10476
+ 5.08134 1.02051 2.10476
+ 4.57308 -.97945 3.05564
+ 4.57308 1.02058 3.05564
+ 3.88909 -.98017 3.88909
+ 3.88909 1.01986 3.88909
+ 3.05564 -.98165 4.57308
+ 3.05564 1.01838 4.57308
+ 2.10476 -.98383 5.08134
+ 2.10476 1.01619 5.08134
+ 1.073 -.98664 5.39432
+ 1.073 1.01338 5.39432
+ -0 -.98997 5.5
+ -0 1.01006 5.5
+ -1.073 -.99367 5.39432
+ -1.073 1.00635 5.39432
+ -2.10476 -.99763 5.08134
+ -2.10476 1.0024 5.08134
+ -3.05564 -1.00167 4.57308
+ -3.05564 .99836 4.57308
+ -3.88909 -1.00565 3.88909
+ -3.88909 .99438 3.88909
+ -4.57308 -1.00941 3.05564
+ -4.57308 .99061 3.05564
+ -5.08134 -1.01282 2.10476
+ -5.08134 .98721 2.10476
+ -5.39432 -1.01573 1.073
+ -5.39432 .9843 1.073
+ -5.5 -1.01803 -0
+ -5.5 .98199 -0
+ -5.39432 -1.01965 -1.073
+ -5.39432 .98038 -1.073
+ -5.08134 -1.02051 -2.10476
+ -5.08134 .97952 -2.10476
+ -4.57308 -1.02058 -3.05564
+ -4.57308 .97945 -3.05564
+ -3.88909 -1.01986 -3.88909
+ -3.88909 .98017 -3.88909
+ -3.05564 -1.01838 -4.57308
+ -3.05564 .98165 -4.57308
+ -2.10476 -1.01619 -5.08134
+ -2.10476 .98383 -5.08134
+ -1.073 -1.01338 -5.39432
+ -1.073 .98664 -5.39432
+ ]
+ }
+ }
+ }
+ ]
+ }
+ DEF dad_Cylinder5 Transform {
+ translation 0 0 -8.4
+ rotation 1 0 0 1.571
+ children [
+ DEF Cylinder5 Shape {
+ appearance Appearance {
+ material USE Green
+ }
+ geometry DEF GeoCylinder5 Cylinder {
+ height 4.250
+ radius 2.000
+ }
+ }
+ ]
+ }
+ DEF dad_Box10 Transform {
+ translation 0 2 -8.4
+ children [
+ DEF Box10 Shape {
+ appearance Appearance {
+ material USE Green
+ }
+ geometry DEF GeoBox10 Box {
+ size .25 1 4.25
+ }
+ }
+ ]
+ }
+ ]
+ }
+ DEF dad_lab_posttop Transform {
+ translation 0 0 -11
+ rotation -1 0 0 1.571
+ children [
+ DEF alpha_posttop Shape {
+ appearance Appearance {
+ material USE Red
+ }
+ geometry DEF GeoCylinder6 Cylinder {
+ height 1.000
+ radius 2.000
+ }
+ }
+ ]
+ }
+ DEF dad_alpha_arm Transform {
+ translation 0 -20.5 -5
+ children [
+ DEF alpha_arm Shape {
+ appearance Appearance {
+ material USE Red
+ }
+ geometry DEF GeoBox7 Box {
+ size 4 1 13
+ }
+ }
+ ]
+ }
+ DEF dad_gamma_arm1 Transform {
+ translation -6 -20.5 0
+ children [
+ DEF gamma_arm1 Shape {
+ appearance Appearance {
+ material USE Red
+ }
+ geometry DEF GeoBox18 Box {
+ size 1 2 .25
+ }
+ }
+ ]
+ }
+ DEF dad_Box12 Transform {
+ translation 0 2 -11
+ children [
+ DEF Box12 Shape {
+ appearance Appearance {
+ material USE Red
+ }
+ geometry DEF GeoBox12 Box {
+ size .25 1 1
+ }
+ }
+ ]
+ }
+ DEF dad_delta_frame Transform {
+ rotation 0 0 1 -.349
+ children [
+ DEF dad_delta_mount Transform {
+ translation 0 0 -14.25
+ rotation -1 0 0 1.571
+ children [
+ DEF delta_mount Shape {
+ appearance Appearance {
+ material DEF Yellow Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .83529 .83529 0
+ }
+ }
+ geometry DEF GeoCylinder2 Cylinder {
+ height 5.500
+ radius 2.000
+ }
+ }
+ ]
+ }
+ DEF dad_delta_arm Transform {
+ translation -10 0 -15
+ children [
+ DEF delta_arm Shape {
+ appearance Appearance {
+ material USE Yellow
+ }
+ geometry DEF GeoBox8 Box {
+ size 20 3 1
+ }
+ }
+ ]
+ }
+ DEF dad_delta_arm0 Transform {
+ translation -20 0 -6.5
+ children [
+ DEF delta_arm0 Shape {
+ appearance Appearance {
+ material USE Yellow
+ }
+ geometry DEF GeoBox6 Box {
+ size 1 3 18
+ }
+ }
+ ]
+ }
+ DEF dad_Cylinder14 Transform {
+ translation -18 0 0
+ rotation 0 0 -1 1.571
+ children [
+ DEF Cylinder14 Shape {
+ appearance Appearance {
+ material USE Yellow
+ }
+ geometry DEF GeoCylinder14 Cylinder {
+ height 3.000
+ radius 1.000
+ }
+ }
+ ]
+ }
+ DEF dad_detector_beam Transform {
+ translation -10 0 0
+ rotation 0 0 1 1.571
+ children [
+ DEF detector_beam Shape {
+ appearance Appearance {
+ material USE White
+ }
+ geometry DEF GeoCylinder21 Cylinder {
+ height 20.000
+ radius 0.050
+ }
+ }
+ ]
+ }
+ DEF dad_Box13 Transform {
+ translation 0 2 -14.25
+ children [
+ DEF Box13 Shape {
+ appearance Appearance {
+ material USE Yellow
+ }
+ geometry DEF GeoBox13 Box {
+ size .25 1 5.5
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
+DEF VP Viewpoint {
+ description "VP"
+ jump TRUE
+ fieldOfView 1.165
+ position 40 40 40
+ orientation -.754 .657 -0 1.001
+}
+DEF dad_gamma_arm2 Transform {
+ translation -9 -22.5 0
+ children [
+ DEF gamma_arm2 Shape {
+ appearance Appearance {
+ material DEF Black Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .20784 .20784 .20784
+ }
+ }
+ geometry DEF GeoBox19 Box {
+ size 1 2 .25
+ }
+ }
+ ]
+}
+DEF dad_gamma_base0 Transform {
+ translation 0 -22.5 0
+ rotation 0 1 0 .349
+ children [
+ DEF gamma_base0 Shape {
+ appearance Appearance {
+ material USE Black
+ }
+ geometry DEF GeoCylinder9 Cylinder {
+ height 2.000
+ radius 9.000
+ }
+ }
+ ]
+}
+DEF dad_floor Transform {
+ translation 0 -23.5 0
+ children [
+ DEF floor Shape {
+ appearance Appearance {
+ material USE Black
+ }
+ geometry DEF GeoBox16 Box {
+ size 60 .2 40
+ }
+ }
+ ]
+}
diff --git a/script/__Lib/diffcalc-2.1/model/sixc.fxw b/script/__Lib/diffcalc-2.1/model/sixc.fxw
new file mode 100755
index 0000000..51d97a4
Binary files /dev/null and b/script/__Lib/diffcalc-2.1/model/sixc.fxw differ
diff --git a/script/__Lib/diffcalc-2.1/model/sixc.wcfg b/script/__Lib/diffcalc-2.1/model/sixc.wcfg
new file mode 100755
index 0000000..2fa6961
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/model/sixc.wcfg
@@ -0,0 +1,6 @@
+dad_alpha_frame rotation pv["DIFFSIM:SIXC:ALPHA.RBV"]
+dad_delta_frame rotation pv["DIFFSIM:SIXC:DELTA.RBV"]
+dad_gamma_frame rotation pv["DIFFSIM:SIXC:GAMMA.RBV"]
+dad_omega_frame rotation pv["DIFFSIM:SIXC:OMEGA.RBV"]
+dad_chi_frame rotation pv["DIFFSIM:SIXC:CHI.RBV"]
+dad_phi_frame rotation pv["DIFFSIM:SIXC:PHI.RBV"]
diff --git a/script/__Lib/diffcalc-2.1/model/sixc.wrl b/script/__Lib/diffcalc-2.1/model/sixc.wrl
new file mode 100755
index 0000000..32ad769
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/model/sixc.wrl
@@ -0,0 +1,1101 @@
+#VRML V2.0 utf8
+WorldInfo {
+ title "fourc"
+ info ["This Web3D Content was created with Flux Studio, a Web3D authoring tool"
+ "www.mediamachines.com"
+ "This Web3D Content was created with Flux Studio, a Web3D authoring tool"]
+}
+## Vizthumbnail Thumb_fourc_wrl36454531255104963.jpg
+DirectionalLight {
+ intensity 1.000
+ ambientIntensity 0.500
+ direction -.621 -.23944 -.74634
+ color 1 1 1
+ on TRUE
+}
+DirectionalLight {
+ intensity 1.000
+ ambientIntensity 0.000
+ direction -.01373 -.46642 -.88446
+ color 1 1 1
+ on TRUE
+}
+DEF lab_x Shape {
+ appearance Appearance {
+ material DEF Rust Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .50196 .50196 .50196
+ }
+ }
+ geometry DEF GeoCylinder15 Cylinder {
+ height 60.000
+ radius 0.020
+ }
+}
+DEF dad_lab_z Transform {
+ rotation 1 0 0 1.571
+ children [
+ DEF lab_z Shape {
+ appearance Appearance {
+ material USE Rust
+ }
+ geometry DEF GeoCylinder17 Cylinder {
+ height 40.000
+ radius 0.020
+ }
+ }
+ ]
+}
+DEF dad_lab_beam Transform {
+ translation 10 0 0
+ rotation 0 0 1 1.571
+ children [
+ DEF lab_beam Shape {
+ appearance Appearance {
+ material DEF White Material {
+ ambientIntensity 0.500
+ shininess 0.100
+ transparency 0.500
+ diffuseColor 1 1 1
+ emissiveColor 1 1 1
+ }
+ }
+ geometry DEF GeoCylinder20 Cylinder {
+ height 20.000
+ radius 0.050
+ }
+ }
+ ]
+}
+DEF dad_lab_beam0 Transform {
+ translation 20 0 0
+ rotation 0 0 1 1.571
+ children [
+ DEF lab_beam0 Shape {
+ appearance Appearance {
+ material DEF Shiny_Black Material {
+ ambientIntensity 0.200
+ shininess 0.100
+ diffuseColor 1 1 1
+ }
+ }
+ geometry DEF GeoCylinder22 Cylinder {
+ height 4.000
+ radius 0.200
+ }
+ }
+ ]
+}
+DEF dad_Cone2 Transform {
+ translation 17.5 0 0
+ rotation 0 0 1 1.571
+ children [
+ DEF Cone2 Shape {
+ appearance Appearance {
+ material USE Shiny_Black
+ }
+ geometry DEF GeoCone2 Cone {
+ height 1.500
+ bottomRadius 0.500
+ }
+ }
+ ]
+}
+DEF dad_lab_y Transform {
+ rotation 0 0 -1 1.571
+ children [
+ DEF lab_y Shape {
+ appearance Appearance {
+ material USE Rust
+ }
+ geometry DEF GeoCylinder16 Cylinder {
+ height 60.000
+ radius 0.020
+ }
+ }
+ ]
+}
+DEF dad_alpha_frame Transform {
+ rotation 0 1 0 .175
+ children [
+ DEF dad_alpha_base Transform {
+ translation 0 -20.5 0
+ children [
+ DEF alpha_base Shape {
+ appearance Appearance {
+ material DEF Red Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .50196 0 0
+ }
+ }
+ geometry DEF GeoCylinder1 Cylinder {
+ height 2.000
+ radius 6.000
+ }
+ }
+ ]
+ }
+ DEF dad_lab_post Transform {
+ translation 0 -10 -11
+ children [
+ DEF alpha_post Shape {
+ appearance Appearance {
+ material USE Red
+ }
+ geometry DEF GeoBox1 Box {
+ size 4 20 1
+ }
+ }
+ ]
+ }
+ DEF dad_omega_frame Transform {
+ rotation 0 0 1 -.175
+ children [
+ DEF dad_chi_frame Transform {
+ rotation 1 0 0 -.785
+ children [
+ DEF dad_phi_frame Transform {
+ rotation 0 0 1 -.349
+ children [
+ DEF dad_Cylinder7 Transform {
+ translation 0 0 -4
+ rotation 1 0 0 1.571
+ children [
+ DEF Cylinder7 Shape {
+ appearance Appearance {
+ material USE White
+ }
+ geometry DEF GeoCylinder7 Cylinder {
+ height 2.000
+ radius 1.000
+ }
+ }
+ ]
+ }
+ DEF dad_Cylinder8 Transform {
+ translation 0 0 -3.5
+ rotation 1 0 0 1.571
+ children [
+ DEF Cylinder8 Shape {
+ appearance Appearance {
+ material USE White
+ }
+ geometry DEF GeoCylinder8 Cylinder {
+ height 6.000
+ radius 0.250
+ }
+ }
+ ]
+ }
+ DEF Box3 Shape {
+ appearance Appearance {
+ material USE White
+ }
+ geometry DEF GeoBox3 Box {
+ size 1 1 1
+ }
+ }
+ DEF dad_Box11 Transform {
+ translation 0 1 -4
+ children [
+ DEF Box11 Shape {
+ appearance Appearance {
+ material USE White
+ }
+ geometry DEF GeoBox11 Box {
+ size .25 .5 2
+ }
+ }
+ ]
+ }
+ DEF Box4 Shape {
+ appearance Appearance {
+ material DEF White_wire Material {
+ emissiveColor 1 1 1
+ }
+ }
+ geometry IndexedLineSet {
+ coordIndex [
+ 0 1 2 0
+ -1 0 2 3
+ 0 -1 1 5
+ 6 1 -1 1
+ 6 2 1 -1
+ 2 6 7 2
+ -1 2 7 3
+ 2 -1 3 7
+ 4 3 -1 3
+ 4 0 3 -1
+ 0 4 5 0
+ -1 0 5 1
+ 0 -1 6 5
+ 4 6 -1 6
+ 4 7 6 -1
+ ]
+ coord Coordinate {
+ point [
+ -.5 .5 -.5
+ -.5 .5 .5
+ .5 .5 .5
+ .5 .5 -.5
+ -.5 -.5 -.5
+ -.5 -.5 .5
+ .5 -.5 .5
+ .5 -.5 -.5
+ ]
+ }
+ }
+ }
+ ]
+ }
+ DEF dad_Box2 Transform {
+ translation 0 0 -5.5
+ children [
+ DEF Box2 Shape {
+ appearance Appearance {
+ material DEF Purple Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .9176 0 .874
+ }
+ }
+ geometry DEF GeoBox2 Box {
+ size 2.8 2.8 1.5
+ }
+ }
+ ]
+ }
+ DEF dad_Box6 Transform {
+ translation 0 1.25 -5.5
+ children [
+ DEF Box6 Shape {
+ appearance Appearance {
+ material USE Purple
+ }
+ geometry DEF GeoBox21 Box {
+ size .25 1 1.5
+ }
+ }
+ ]
+ }
+ ]
+ }
+ DEF dad_Cylinder2 Transform {
+ rotation 0 0 1 1.571
+ scale 1.1 1.5 1.1
+ children [
+ DEF Cylinder2 Shape {
+ appearance Appearance {
+ material DEF Green Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .00784 .65098 .02353
+ }
+ }
+ geometry DEF Cylinder2_Geo IndexedFaceSet {
+ creaseAngle 0.524
+ coordIndex [
+ 49 50 48 -1
+ 48 50 51 -1
+ 48 51 47 -1
+ 47 51 20 -1
+ 47 20 21 -1
+ 12 13 56 -1
+ 56 13 14 -1
+ 56 14 55 -1
+ 55 14 15 -1
+ 55 15 54 -1
+ 54 15 16 -1
+ 54 16 53 -1
+ 53 16 17 -1
+ 53 17 52 -1
+ 52 17 18 -1
+ 52 18 51 -1
+ 51 18 19 -1
+ 51 19 20 -1
+ 5 6 62 -1
+ 62 6 7 -1
+ 62 7 61 -1
+ 61 7 8 -1
+ 61 8 60 -1
+ 60 8 9 -1
+ 60 9 59 -1
+ 59 9 10 -1
+ 59 10 58 -1
+ 58 10 11 -1
+ 58 11 57 -1
+ 57 11 12 -1
+ 57 12 56 -1
+ 5 63 4 -1
+ 4 63 64 -1
+ 4 64 3 -1
+ 3 64 1 -1
+ 3 1 2 -1
+ 2 1 35 -1
+ 2 35 36 -1
+ 28 41 27 -1
+ 27 41 42 -1
+ 27 42 26 -1
+ 26 42 43 -1
+ 26 43 25 -1
+ 25 43 44 -1
+ 25 44 24 -1
+ 24 44 45 -1
+ 24 45 23 -1
+ 23 45 46 -1
+ 23 46 22 -1
+ 22 46 47 -1
+ 22 47 21 -1
+ 34 36 33 -1
+ 33 36 37 -1
+ 33 37 32 -1
+ 32 37 38 -1
+ 32 38 31 -1
+ 31 38 39 -1
+ 31 39 30 -1
+ 30 39 40 -1
+ 30 40 29 -1
+ 29 40 41 -1
+ 29 41 28 -1
+ 1 64 0 -1
+ 2 36 34 -1
+ 63 5 62 -1
+ 95 96 97 -1
+ 97 96 65 -1
+ 97 65 129 -1
+ 129 65 66 -1
+ 129 66 128 -1
+ 128 66 67 -1
+ 128 67 127 -1
+ 127 67 126 -1
+ 104 105 88 -1
+ 88 105 106 -1
+ 88 106 87 -1
+ 87 106 107 -1
+ 87 107 86 -1
+ 86 107 108 -1
+ 86 108 85 -1
+ 85 108 109 -1
+ 85 109 84 -1
+ 84 109 110 -1
+ 84 110 83 -1
+ 83 110 111 -1
+ 83 111 82 -1
+ 82 111 112 -1
+ 82 112 113 -1
+ 98 99 93 -1
+ 93 99 100 -1
+ 93 100 92 -1
+ 92 100 101 -1
+ 92 101 91 -1
+ 91 101 102 -1
+ 91 102 90 -1
+ 90 102 103 -1
+ 90 103 89 -1
+ 89 103 104 -1
+ 89 104 88 -1
+ 94 95 97 -1
+ 94 97 98 -1
+ 94 98 93 -1
+ 81 82 78 -1
+ 81 78 79 -1
+ 81 79 80 -1
+ 78 82 113 -1
+ 78 113 114 -1
+ 78 114 115 -1
+ 78 115 77 -1
+ 120 73 119 -1
+ 119 73 74 -1
+ 119 74 118 -1
+ 118 74 75 -1
+ 118 75 117 -1
+ 117 75 76 -1
+ 117 76 116 -1
+ 116 76 77 -1
+ 116 77 115 -1
+ 126 68 125 -1
+ 125 68 69 -1
+ 125 69 124 -1
+ 124 69 70 -1
+ 124 70 123 -1
+ 123 70 71 -1
+ 123 71 122 -1
+ 122 71 72 -1
+ 122 72 121 -1
+ 121 72 73 -1
+ 121 73 120 -1
+ 126 67 68 -1
+ 0 64 65 -1
+ 0 65 96 -1
+ 64 63 66 -1
+ 64 66 65 -1
+ 63 62 67 -1
+ 63 67 66 -1
+ 62 61 68 -1
+ 62 68 67 -1
+ 61 60 69 -1
+ 61 69 68 -1
+ 60 59 70 -1
+ 60 70 69 -1
+ 59 58 71 -1
+ 59 71 70 -1
+ 58 57 72 -1
+ 58 72 71 -1
+ 57 56 73 -1
+ 57 73 72 -1
+ 56 55 74 -1
+ 56 74 73 -1
+ 55 54 75 -1
+ 55 75 74 -1
+ 54 53 76 -1
+ 54 76 75 -1
+ 53 52 77 -1
+ 53 77 76 -1
+ 52 51 78 -1
+ 52 78 77 -1
+ 51 50 79 -1
+ 51 79 78 -1
+ 50 49 80 -1
+ 50 80 79 -1
+ 49 48 81 -1
+ 49 81 80 -1
+ 48 47 82 -1
+ 48 82 81 -1
+ 47 46 83 -1
+ 47 83 82 -1
+ 46 45 84 -1
+ 46 84 83 -1
+ 45 44 85 -1
+ 45 85 84 -1
+ 44 43 86 -1
+ 44 86 85 -1
+ 43 42 87 -1
+ 43 87 86 -1
+ 42 41 88 -1
+ 42 88 87 -1
+ 41 40 89 -1
+ 41 89 88 -1
+ 40 39 90 -1
+ 40 90 89 -1
+ 39 38 91 -1
+ 39 91 90 -1
+ 38 37 92 -1
+ 38 92 91 -1
+ 37 36 93 -1
+ 37 93 92 -1
+ 36 35 94 -1
+ 36 94 93 -1
+ 35 1 95 -1
+ 35 95 94 -1
+ 1 0 96 -1
+ 1 96 95 -1
+ ]
+ coord DEF Cylinder2_Coord Coordinate {
+ point [
+ 0 1 -6
+ -1.17054 1 -5.88471
+ -1.07212 1 -5.3899
+ -1.05189 1 -5.39596
+ .02145 1 -5.49809
+ 1.09408 1 -5.38886
+ 2.1248 1 -5.07248
+ 3.07398 1 -4.56108
+ 3.90517 1 -3.87434
+ 4.5864 1 -3.03864
+ 5.0915 1 -2.08609
+ 5.40107 1 -1.0533
+ 5.5032 1 .02003
+ 5.39398 1 1.09267
+ 5.07759 1 2.12338
+ 4.5662 1 3.07257
+ 3.87945 1 3.90375
+ 3.04375 1 4.58498
+ 2.0912 1 5.09009
+ 1.05842 1 5.39966
+ -.01492 1 5.50179
+ -1.08755 1 5.39256
+ -2.11827 1 5.07617
+ -3.06746 1 4.56478
+ -3.89864 1 3.87804
+ -4.57987 1 3.04233
+ -5.08498 1 2.08979
+ -5.39454 1 1.057
+ -5.49668 1 -.01633
+ -5.38745 1 -1.08897
+ -5.07106 1 -2.11968
+ -4.55967 1 -3.06887
+ -3.87292 1 -3.90005
+ -3.03722 1 -4.58129
+ -2.08467 1 -5.08639
+ -2.2961 1 -5.54328
+ -3.33342 1 -4.98882
+ -4.24264 1 -4.24264
+ -4.98882 1 -3.33342
+ -5.54328 1 -2.2961
+ -5.88471 1 -1.17054
+ -6 1 -0
+ -5.88471 1 1.17054
+ -5.54328 1 2.2961
+ -4.98882 1 3.33342
+ -4.24264 1 4.24264
+ -3.33342 1 4.98882
+ -2.2961 1 5.54328
+ -1.17054 1 5.88471
+ -0 1 6
+ 1.17054 1 5.88471
+ 2.2961 1 5.54328
+ 3.33342 1 4.98882
+ 4.24264 1 4.24264
+ 4.98882 1 3.33342
+ 5.54328 1 2.2961
+ 5.88471 1 1.17054
+ 6 1 0
+ 5.88471 1 -1.17054
+ 5.54328 1 -2.2961
+ 4.98882 1 -3.33342
+ 4.24264 1 -4.24264
+ 3.33342 1 -4.98882
+ 2.2961 1 -5.54328
+ 1.17054 1 -5.88471
+ 1.17054 -1 -5.88471
+ 2.2961 -1 -5.54328
+ 3.33342 -1 -4.98882
+ 4.24264 -1 -4.24264
+ 4.98882 -1 -3.33342
+ 5.54328 -1 -2.2961
+ 5.88471 -1 -1.17054
+ 6 -1 -0
+ 5.88471 -1 1.17054
+ 5.54328 -1 2.2961
+ 4.98882 -1 3.33342
+ 4.24264 -1 4.24264
+ 3.33342 -1 4.98882
+ 2.2961 -1 5.54328
+ 1.17054 -1 5.88471
+ -0 -1 6
+ -1.17054 -1 5.88471
+ -2.2961 -1 5.54328
+ -3.33342 -1 4.98882
+ -4.24264 -1 4.24264
+ -4.98882 -1 3.33342
+ -5.54328 -1 2.2961
+ -5.88471 -1 1.17054
+ -6 -1 -0
+ -5.88471 -1 -1.17054
+ -5.54328 -1 -2.2961
+ -4.98882 -1 -3.33342
+ -4.24264 -1 -4.24264
+ -3.33342 -1 -4.98882
+ -2.2961 -1 -5.54328
+ -1.17054 -1 -5.88471
+ 0 -1 -6
+ 0 -1 -5.50037
+ -1.05842 -1 -5.39966
+ -2.0912 -1 -5.09009
+ -3.04375 -1 -4.58498
+ -3.87945 -1 -3.90375
+ -4.5662 -1 -3.07257
+ -5.07759 -1 -2.12338
+ -5.39398 -1 -1.09267
+ -5.5032 -1 -.02003
+ -5.40107 -1 1.0533
+ -5.0915 -1 2.08609
+ -4.5864 -1 3.03864
+ -3.90517 -1 3.87434
+ -3.07398 -1 4.56108
+ -2.1248 -1 5.07248
+ -1.09408 -1 5.38886
+ -.02145 -1 5.49809
+ 1.05189 -1 5.39596
+ 2.08467 -1 5.08639
+ 3.03722 -1 4.58129
+ 3.87292 -1 3.90005
+ 4.55967 -1 3.06887
+ 5.07106 -1 2.11968
+ 5.38745 -1 1.08897
+ 5.49668 -1 .01633
+ 5.39454 -1 -1.057
+ 5.08498 -1 -2.08979
+ 4.57987 -1 -3.04233
+ 3.89864 -1 -3.87804
+ 3.06746 -1 -4.56478
+ 2.11827 -1 -5.07617
+ 1.08755 -1 -5.39256
+ .01492 -1 -5.50179
+ ]
+ }
+ }
+ }
+ ]
+ }
+ DEF dad_Cylinder3 Transform {
+ rotation 0 0 1 1.571
+ scale 1.1 1.5 1.1
+ children [
+ DEF Cylinder3 Shape {
+ appearance Appearance {
+ material USE Green
+ }
+ geometry DEF Cylinder3_Geo IndexedFaceSet {
+ creaseAngle 0.524
+ coordIndex [
+ 0 1 2 -1
+ 0 2 3 -1
+ 4 5 1 -1
+ 4 1 0 -1
+ 6 7 5 -1
+ 6 5 4 -1
+ 8 9 7 -1
+ 8 7 6 -1
+ 10 11 9 -1
+ 10 9 8 -1
+ 12 13 11 -1
+ 12 11 10 -1
+ 14 15 13 -1
+ 14 13 12 -1
+ 16 17 15 -1
+ 16 15 14 -1
+ 18 19 17 -1
+ 18 17 16 -1
+ 20 21 19 -1
+ 20 19 18 -1
+ 22 23 21 -1
+ 22 21 20 -1
+ 24 25 23 -1
+ 24 23 22 -1
+ 26 27 25 -1
+ 26 25 24 -1
+ 28 29 27 -1
+ 28 27 26 -1
+ 30 31 29 -1
+ 30 29 28 -1
+ 32 33 31 -1
+ 32 31 30 -1
+ 34 35 33 -1
+ 34 33 32 -1
+ 36 37 35 -1
+ 36 35 34 -1
+ 38 39 37 -1
+ 38 37 36 -1
+ 40 41 39 -1
+ 40 39 38 -1
+ 42 43 41 -1
+ 42 41 40 -1
+ 44 45 43 -1
+ 44 43 42 -1
+ 46 47 45 -1
+ 46 45 44 -1
+ 48 49 47 -1
+ 48 47 46 -1
+ 50 51 49 -1
+ 50 49 48 -1
+ 52 53 51 -1
+ 52 51 50 -1
+ 54 55 53 -1
+ 54 53 52 -1
+ 56 57 55 -1
+ 56 55 54 -1
+ 58 59 57 -1
+ 58 57 56 -1
+ 60 61 59 -1
+ 60 59 58 -1
+ 62 63 61 -1
+ 62 61 60 -1
+ 3 2 63 -1
+ 3 63 62 -1
+ ]
+ coord DEF Cylinder3_Coord Coordinate {
+ point [
+ 1.073 -1.00635 -5.39432
+ 1.073 .99367 -5.39432
+ 0 .98997 -5.5
+ 0 -1.01006 -5.5
+ 2.10476 -1.0024 -5.08134
+ 2.10476 .99763 -5.08134
+ 3.05564 -.99836 -4.57308
+ 3.05564 1.00167 -4.57308
+ 3.88909 -.99438 -3.88909
+ 3.88909 1.00565 -3.88909
+ 4.57308 -.99061 -3.05564
+ 4.57308 1.00941 -3.05564
+ 5.08134 -.98721 -2.10476
+ 5.08134 1.01282 -2.10476
+ 5.39432 -.9843 -1.073
+ 5.39432 1.01573 -1.073
+ 5.5 -.98199 -0
+ 5.5 1.01803 -0
+ 5.39432 -.98038 1.073
+ 5.39432 1.01965 1.073
+ 5.08134 -.97952 2.10476
+ 5.08134 1.02051 2.10476
+ 4.57308 -.97945 3.05564
+ 4.57308 1.02058 3.05564
+ 3.88909 -.98017 3.88909
+ 3.88909 1.01986 3.88909
+ 3.05564 -.98165 4.57308
+ 3.05564 1.01838 4.57308
+ 2.10476 -.98383 5.08134
+ 2.10476 1.01619 5.08134
+ 1.073 -.98664 5.39432
+ 1.073 1.01338 5.39432
+ -0 -.98997 5.5
+ -0 1.01006 5.5
+ -1.073 -.99367 5.39432
+ -1.073 1.00635 5.39432
+ -2.10476 -.99763 5.08134
+ -2.10476 1.0024 5.08134
+ -3.05564 -1.00167 4.57308
+ -3.05564 .99836 4.57308
+ -3.88909 -1.00565 3.88909
+ -3.88909 .99438 3.88909
+ -4.57308 -1.00941 3.05564
+ -4.57308 .99061 3.05564
+ -5.08134 -1.01282 2.10476
+ -5.08134 .98721 2.10476
+ -5.39432 -1.01573 1.073
+ -5.39432 .9843 1.073
+ -5.5 -1.01803 -0
+ -5.5 .98199 -0
+ -5.39432 -1.01965 -1.073
+ -5.39432 .98038 -1.073
+ -5.08134 -1.02051 -2.10476
+ -5.08134 .97952 -2.10476
+ -4.57308 -1.02058 -3.05564
+ -4.57308 .97945 -3.05564
+ -3.88909 -1.01986 -3.88909
+ -3.88909 .98017 -3.88909
+ -3.05564 -1.01838 -4.57308
+ -3.05564 .98165 -4.57308
+ -2.10476 -1.01619 -5.08134
+ -2.10476 .98383 -5.08134
+ -1.073 -1.01338 -5.39432
+ -1.073 .98664 -5.39432
+ ]
+ }
+ }
+ }
+ ]
+ }
+ DEF dad_Cylinder5 Transform {
+ translation 0 0 -8.4
+ rotation 1 0 0 1.571
+ children [
+ DEF Cylinder5 Shape {
+ appearance Appearance {
+ material USE Green
+ }
+ geometry DEF GeoCylinder5 Cylinder {
+ height 4.250
+ radius 2.000
+ }
+ }
+ ]
+ }
+ DEF dad_Box10 Transform {
+ translation 0 2 -8.4
+ children [
+ DEF Box10 Shape {
+ appearance Appearance {
+ material USE Green
+ }
+ geometry DEF GeoBox10 Box {
+ size .25 1 4.25
+ }
+ }
+ ]
+ }
+ ]
+ }
+ DEF dad_lab_posttop Transform {
+ translation 0 0 -11
+ rotation -1 0 0 1.571
+ children [
+ DEF alpha_posttop Shape {
+ appearance Appearance {
+ material USE Red
+ }
+ geometry DEF GeoCylinder6 Cylinder {
+ height 1.000
+ radius 2.000
+ }
+ }
+ ]
+ }
+ DEF dad_alpha_arm Transform {
+ translation 0 -20.5 -5
+ children [
+ DEF alpha_arm Shape {
+ appearance Appearance {
+ material USE Red
+ }
+ geometry DEF GeoBox7 Box {
+ size 4 1 13
+ }
+ }
+ ]
+ }
+ DEF dad_gamma_arm1 Transform {
+ translation -6 -20.5 0
+ children [
+ DEF gamma_arm1 Shape {
+ appearance Appearance {
+ material USE Red
+ }
+ geometry DEF GeoBox18 Box {
+ size 1 2 .25
+ }
+ }
+ ]
+ }
+ DEF dad_Box12 Transform {
+ translation 0 2 -11
+ children [
+ DEF Box12 Shape {
+ appearance Appearance {
+ material USE Red
+ }
+ geometry DEF GeoBox12 Box {
+ size .25 1 1
+ }
+ }
+ ]
+ }
+ ]
+}
+DEF dad_gamma_frame Transform {
+ rotation 0 1 0 .349
+ children [
+ DEF dad_lab_posttop0 Transform {
+ translation 0 0 -12.5
+ rotation -1 0 0 1.571
+ children [
+ DEF gamma_posttop Shape {
+ appearance Appearance {
+ material DEF Blue Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor 0 0 .62745
+ }
+ }
+ geometry DEF GeoCylinder4 Cylinder {
+ height 1.000
+ radius 2.000
+ }
+ }
+ ]
+ }
+ DEF dad_lab_post0 Transform {
+ translation 0 -11 -12.5
+ children [
+ DEF gamma_post Shape {
+ appearance Appearance {
+ material USE Blue
+ }
+ geometry DEF GeoBox5 Box {
+ size 4 22 1
+ }
+ }
+ ]
+ }
+ DEF dad_gamma_base Transform {
+ translation 0 -22.5 0
+ children [
+ DEF gamma_base Shape {
+ appearance Appearance {
+ material USE Blue
+ }
+ geometry DEF GeoCylinder3 Cylinder {
+ height 2.000
+ radius 8.000
+ }
+ }
+ ]
+ }
+ DEF dad_gamma_arm Transform {
+ translation 0 -22.5 -6.5
+ children [
+ DEF gamma_arm Shape {
+ appearance Appearance {
+ material USE Blue
+ }
+ geometry DEF GeoBox4 Box {
+ size 4 1 13
+ }
+ }
+ ]
+ }
+ DEF dad_delta_frame Transform {
+ rotation 0 0 1 -.524
+ children [
+ DEF dad_delta_mount Transform {
+ translation 0 0 -15
+ rotation -1 0 0 1.571
+ children [
+ DEF delta_mount Shape {
+ appearance Appearance {
+ material DEF Yellow Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .83529 .83529 0
+ }
+ }
+ geometry DEF GeoCylinder2 Cylinder {
+ height 4.000
+ radius 2.000
+ }
+ }
+ ]
+ }
+ DEF dad_delta_arm Transform {
+ translation -10 0 -15
+ children [
+ DEF delta_arm Shape {
+ appearance Appearance {
+ material USE Yellow
+ }
+ geometry DEF GeoBox8 Box {
+ size 20 3 1
+ }
+ }
+ ]
+ }
+ DEF dad_delta_arm0 Transform {
+ translation -20 0 -6.5
+ children [
+ DEF delta_arm0 Shape {
+ appearance Appearance {
+ material USE Yellow
+ }
+ geometry DEF GeoBox6 Box {
+ size 1 3 18
+ }
+ }
+ ]
+ }
+ DEF dad_Cylinder14 Transform {
+ translation -18 0 0
+ rotation .577 -.577 .577 2.094
+ children [
+ DEF Cylinder14 Shape {
+ appearance Appearance {
+ material USE Yellow
+ }
+ geometry DEF GeoCylinder14 Cylinder {
+ height 3.000
+ radius 1.000
+ }
+ }
+ ]
+ }
+ DEF dad_detector_beam Transform {
+ translation -10 0 0
+ rotation 0 0 1 1.571
+ children [
+ DEF detector_beam Shape {
+ appearance Appearance {
+ material USE White
+ }
+ geometry DEF GeoCylinder21 Cylinder {
+ height 20.000
+ radius 0.050
+ }
+ }
+ ]
+ }
+ DEF dad_Box13 Transform {
+ translation 0 2 -15
+ children [
+ DEF Box13 Shape {
+ appearance Appearance {
+ material USE Yellow
+ }
+ geometry DEF GeoBox13 Box {
+ size .25 1 4
+ }
+ }
+ ]
+ }
+ ]
+ }
+ DEF dad_gamma_arm0 Transform {
+ translation -8 -22.5 0
+ children [
+ DEF gamma_arm0 Shape {
+ appearance Appearance {
+ material USE Blue
+ }
+ geometry DEF GeoBox17 Box {
+ size 1 2 .25
+ }
+ }
+ ]
+ }
+ DEF dad_Box14 Transform {
+ translation 0 2 -12.5
+ children [
+ DEF Box14 Shape {
+ appearance Appearance {
+ material USE Blue
+ }
+ geometry DEF GeoBox14 Box {
+ size .25 1 1
+ }
+ }
+ ]
+ }
+ ]
+}
+DEF VP Viewpoint {
+ description "VP"
+ jump TRUE
+ fieldOfView 1.165
+ position 40 40 40
+ orientation -.754 .657 -0 1.001
+}
+DEF dad_gamma_arm2 Transform {
+ translation -9 -24.5 0
+ children [
+ DEF gamma_arm2 Shape {
+ appearance Appearance {
+ material DEF Black Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .20784 .20784 .20784
+ }
+ }
+ geometry DEF GeoBox19 Box {
+ size 1 2 .25
+ }
+ }
+ ]
+}
+DEF dad_gamma_base0 Transform {
+ translation 0 -24.5 0
+ rotation 0 1 0 .349
+ children [
+ DEF gamma_base0 Shape {
+ appearance Appearance {
+ material USE Black
+ }
+ geometry DEF GeoCylinder9 Cylinder {
+ height 2.000
+ radius 9.000
+ }
+ }
+ ]
+}
+DEF dad_floor Transform {
+ translation 0 -25.5 0
+ children [
+ DEF floor Shape {
+ appearance Appearance {
+ material USE Black
+ }
+ geometry DEF GeoBox16 Box {
+ size 60 .2 40
+ }
+ }
+ ]
+}
diff --git a/script/__Lib/diffcalc-2.1/model/sixc_horizontal.fxw b/script/__Lib/diffcalc-2.1/model/sixc_horizontal.fxw
new file mode 100755
index 0000000..9eea919
Binary files /dev/null and b/script/__Lib/diffcalc-2.1/model/sixc_horizontal.fxw differ
diff --git a/script/__Lib/diffcalc-2.1/model/sixc_horizontal.wrl b/script/__Lib/diffcalc-2.1/model/sixc_horizontal.wrl
new file mode 100755
index 0000000..3ec9d25
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/model/sixc_horizontal.wrl
@@ -0,0 +1,1106 @@
+#VRML V2.0 utf8
+WorldInfo {
+ title "fourc"
+ info ["This Web3D Content was created with Flux Studio, a Web3D authoring tool"
+ "www.mediamachines.com"
+ "This Web3D Content was created with Flux Studio, a Web3D authoring tool"]
+}
+## Vizthumbnail Thumb_sixc_horizontal_wrl8844311285236111.jpg
+DEF dad_GROUND_ROTATED Transform {
+ rotation 1 0 0 1.571
+ children [
+ DirectionalLight {
+ intensity 1.000
+ ambientIntensity 0.500
+ direction -.621 -.23944 -.74634
+ color 1 1 1
+ on TRUE
+ }
+ DirectionalLight {
+ intensity 1.000
+ ambientIntensity 0.000
+ direction -.01373 -.46642 -.88446
+ color 1 1 1
+ on TRUE
+ }
+ DEF lab_x Shape {
+ appearance Appearance {
+ material DEF Rust Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .50196 .50196 .50196
+ }
+ }
+ geometry DEF GeoCylinder15 Cylinder {
+ height 60.000
+ radius 0.020
+ }
+ }
+ DEF dad_lab_z Transform {
+ rotation 1 0 0 1.571
+ children [
+ DEF lab_z Shape {
+ appearance Appearance {
+ material USE Rust
+ }
+ geometry DEF GeoCylinder17 Cylinder {
+ height 40.000
+ radius 0.020
+ }
+ }
+ ]
+ }
+ DEF dad_lab_beam Transform {
+ translation 10 0 0
+ rotation 0 0 1 1.571
+ children [
+ DEF lab_beam Shape {
+ appearance Appearance {
+ material DEF White Material {
+ ambientIntensity 0.500
+ shininess 0.100
+ transparency 0.500
+ diffuseColor 1 1 1
+ emissiveColor 1 1 1
+ }
+ }
+ geometry DEF GeoCylinder20 Cylinder {
+ height 20.000
+ radius 0.050
+ }
+ }
+ ]
+ }
+ DEF dad_lab_beam0 Transform {
+ translation 20 0 0
+ rotation 0 0 1 1.571
+ children [
+ DEF lab_beam0 Shape {
+ appearance Appearance {
+ material DEF Shiny_Black Material {
+ ambientIntensity 0.200
+ shininess 0.100
+ diffuseColor 1 1 1
+ }
+ }
+ geometry DEF GeoCylinder22 Cylinder {
+ height 4.000
+ radius 0.200
+ }
+ }
+ ]
+ }
+ DEF dad_Cone2 Transform {
+ translation 17.5 0 0
+ rotation 0 0 1 1.571
+ children [
+ DEF Cone2 Shape {
+ appearance Appearance {
+ material USE Shiny_Black
+ }
+ geometry DEF GeoCone2 Cone {
+ height 1.500
+ bottomRadius 0.500
+ }
+ }
+ ]
+ }
+ DEF dad_lab_y Transform {
+ rotation 0 0 -1 1.571
+ children [
+ DEF lab_y Shape {
+ appearance Appearance {
+ material USE Rust
+ }
+ geometry DEF GeoCylinder16 Cylinder {
+ height 60.000
+ radius 0.020
+ }
+ }
+ ]
+ }
+ DEF dad_alpha_frame Transform {
+ rotation 0 1 0 .175
+ children [
+ DEF dad_alpha_base Transform {
+ translation 0 -20.5 0
+ children [
+ DEF alpha_base Shape {
+ appearance Appearance {
+ material DEF Red Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .50196 0 0
+ }
+ }
+ geometry DEF GeoCylinder1 Cylinder {
+ height 2.000
+ radius 6.000
+ }
+ }
+ ]
+ }
+ DEF dad_lab_post Transform {
+ translation 0 -10 -11
+ children [
+ DEF alpha_post Shape {
+ appearance Appearance {
+ material USE Red
+ }
+ geometry DEF GeoBox1 Box {
+ size 4 20 1
+ }
+ }
+ ]
+ }
+ DEF dad_omega_frame Transform {
+ rotation 0 0 1 -.175
+ children [
+ DEF dad_chi_frame Transform {
+ rotation 1 0 0 -.785
+ children [
+ DEF dad_phi_frame Transform {
+ rotation 0 0 1 -.349
+ children [
+ DEF dad_Cylinder7 Transform {
+ translation 0 0 -4
+ rotation 1 0 0 1.571
+ children [
+ DEF Cylinder7 Shape {
+ appearance Appearance {
+ material USE White
+ }
+ geometry DEF GeoCylinder7 Cylinder {
+ height 2.000
+ radius 1.000
+ }
+ }
+ ]
+ }
+ DEF dad_Cylinder8 Transform {
+ translation 0 0 -3.5
+ rotation 1 0 0 1.571
+ children [
+ DEF Cylinder8 Shape {
+ appearance Appearance {
+ material USE White
+ }
+ geometry DEF GeoCylinder8 Cylinder {
+ height 6.000
+ radius 0.250
+ }
+ }
+ ]
+ }
+ DEF Box3 Shape {
+ appearance Appearance {
+ material USE White
+ }
+ geometry DEF GeoBox3 Box {
+ size 1 1 1
+ }
+ }
+ DEF dad_Box11 Transform {
+ translation 0 1 -4
+ children [
+ DEF Box11 Shape {
+ appearance Appearance {
+ material USE White
+ }
+ geometry DEF GeoBox11 Box {
+ size .25 .5 2
+ }
+ }
+ ]
+ }
+ DEF Box4 Shape {
+ appearance Appearance {
+ material DEF White_wire Material {
+ emissiveColor 1 1 1
+ }
+ }
+ geometry IndexedLineSet {
+ coordIndex [
+ 0 1 2 0
+ -1 0 2 3
+ 0 -1 1 5
+ 6 1 -1 1
+ 6 2 1 -1
+ 2 6 7 2
+ -1 2 7 3
+ 2 -1 3 7
+ 4 3 -1 3
+ 4 0 3 -1
+ 0 4 5 0
+ -1 0 5 1
+ 0 -1 6 5
+ 4 6 -1 6
+ 4 7 6 -1
+ ]
+ coord Coordinate {
+ point [
+ -.5 .5 -.5
+ -.5 .5 .5
+ .5 .5 .5
+ .5 .5 -.5
+ -.5 -.5 -.5
+ -.5 -.5 .5
+ .5 -.5 .5
+ .5 -.5 -.5
+ ]
+ }
+ }
+ }
+ ]
+ }
+ DEF dad_Box2 Transform {
+ translation 0 0 -5.5
+ children [
+ DEF Box2 Shape {
+ appearance Appearance {
+ material DEF Purple Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .9176 0 .874
+ }
+ }
+ geometry DEF GeoBox2 Box {
+ size 2.8 2.8 1.5
+ }
+ }
+ ]
+ }
+ DEF dad_Box6 Transform {
+ translation 0 1.25 -5.5
+ children [
+ DEF Box6 Shape {
+ appearance Appearance {
+ material USE Purple
+ }
+ geometry DEF GeoBox21 Box {
+ size .25 1 1.5
+ }
+ }
+ ]
+ }
+ ]
+ }
+ DEF dad_Cylinder2 Transform {
+ rotation 0 0 1 1.571
+ scale 1.1 1.5 1.1
+ children [
+ DEF Cylinder2 Shape {
+ appearance Appearance {
+ material DEF Green Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .00784 .65098 .02353
+ }
+ }
+ geometry DEF Cylinder2_Geo IndexedFaceSet {
+ creaseAngle 0.524
+ coordIndex [
+ 49 50 48 -1
+ 48 50 51 -1
+ 48 51 47 -1
+ 47 51 20 -1
+ 47 20 21 -1
+ 12 13 56 -1
+ 56 13 14 -1
+ 56 14 55 -1
+ 55 14 15 -1
+ 55 15 54 -1
+ 54 15 16 -1
+ 54 16 53 -1
+ 53 16 17 -1
+ 53 17 52 -1
+ 52 17 18 -1
+ 52 18 51 -1
+ 51 18 19 -1
+ 51 19 20 -1
+ 5 6 62 -1
+ 62 6 7 -1
+ 62 7 61 -1
+ 61 7 8 -1
+ 61 8 60 -1
+ 60 8 9 -1
+ 60 9 59 -1
+ 59 9 10 -1
+ 59 10 58 -1
+ 58 10 11 -1
+ 58 11 57 -1
+ 57 11 12 -1
+ 57 12 56 -1
+ 5 63 4 -1
+ 4 63 64 -1
+ 4 64 3 -1
+ 3 64 1 -1
+ 3 1 2 -1
+ 2 1 35 -1
+ 2 35 36 -1
+ 28 41 27 -1
+ 27 41 42 -1
+ 27 42 26 -1
+ 26 42 43 -1
+ 26 43 25 -1
+ 25 43 44 -1
+ 25 44 24 -1
+ 24 44 45 -1
+ 24 45 23 -1
+ 23 45 46 -1
+ 23 46 22 -1
+ 22 46 47 -1
+ 22 47 21 -1
+ 34 36 33 -1
+ 33 36 37 -1
+ 33 37 32 -1
+ 32 37 38 -1
+ 32 38 31 -1
+ 31 38 39 -1
+ 31 39 30 -1
+ 30 39 40 -1
+ 30 40 29 -1
+ 29 40 41 -1
+ 29 41 28 -1
+ 1 64 0 -1
+ 2 36 34 -1
+ 63 5 62 -1
+ 95 96 97 -1
+ 97 96 65 -1
+ 97 65 129 -1
+ 129 65 66 -1
+ 129 66 128 -1
+ 128 66 67 -1
+ 128 67 127 -1
+ 127 67 126 -1
+ 104 105 88 -1
+ 88 105 106 -1
+ 88 106 87 -1
+ 87 106 107 -1
+ 87 107 86 -1
+ 86 107 108 -1
+ 86 108 85 -1
+ 85 108 109 -1
+ 85 109 84 -1
+ 84 109 110 -1
+ 84 110 83 -1
+ 83 110 111 -1
+ 83 111 82 -1
+ 82 111 112 -1
+ 82 112 113 -1
+ 98 99 93 -1
+ 93 99 100 -1
+ 93 100 92 -1
+ 92 100 101 -1
+ 92 101 91 -1
+ 91 101 102 -1
+ 91 102 90 -1
+ 90 102 103 -1
+ 90 103 89 -1
+ 89 103 104 -1
+ 89 104 88 -1
+ 94 95 97 -1
+ 94 97 98 -1
+ 94 98 93 -1
+ 81 82 78 -1
+ 81 78 79 -1
+ 81 79 80 -1
+ 78 82 113 -1
+ 78 113 114 -1
+ 78 114 115 -1
+ 78 115 77 -1
+ 120 73 119 -1
+ 119 73 74 -1
+ 119 74 118 -1
+ 118 74 75 -1
+ 118 75 117 -1
+ 117 75 76 -1
+ 117 76 116 -1
+ 116 76 77 -1
+ 116 77 115 -1
+ 126 68 125 -1
+ 125 68 69 -1
+ 125 69 124 -1
+ 124 69 70 -1
+ 124 70 123 -1
+ 123 70 71 -1
+ 123 71 122 -1
+ 122 71 72 -1
+ 122 72 121 -1
+ 121 72 73 -1
+ 121 73 120 -1
+ 126 67 68 -1
+ 0 64 65 -1
+ 0 65 96 -1
+ 64 63 66 -1
+ 64 66 65 -1
+ 63 62 67 -1
+ 63 67 66 -1
+ 62 61 68 -1
+ 62 68 67 -1
+ 61 60 69 -1
+ 61 69 68 -1
+ 60 59 70 -1
+ 60 70 69 -1
+ 59 58 71 -1
+ 59 71 70 -1
+ 58 57 72 -1
+ 58 72 71 -1
+ 57 56 73 -1
+ 57 73 72 -1
+ 56 55 74 -1
+ 56 74 73 -1
+ 55 54 75 -1
+ 55 75 74 -1
+ 54 53 76 -1
+ 54 76 75 -1
+ 53 52 77 -1
+ 53 77 76 -1
+ 52 51 78 -1
+ 52 78 77 -1
+ 51 50 79 -1
+ 51 79 78 -1
+ 50 49 80 -1
+ 50 80 79 -1
+ 49 48 81 -1
+ 49 81 80 -1
+ 48 47 82 -1
+ 48 82 81 -1
+ 47 46 83 -1
+ 47 83 82 -1
+ 46 45 84 -1
+ 46 84 83 -1
+ 45 44 85 -1
+ 45 85 84 -1
+ 44 43 86 -1
+ 44 86 85 -1
+ 43 42 87 -1
+ 43 87 86 -1
+ 42 41 88 -1
+ 42 88 87 -1
+ 41 40 89 -1
+ 41 89 88 -1
+ 40 39 90 -1
+ 40 90 89 -1
+ 39 38 91 -1
+ 39 91 90 -1
+ 38 37 92 -1
+ 38 92 91 -1
+ 37 36 93 -1
+ 37 93 92 -1
+ 36 35 94 -1
+ 36 94 93 -1
+ 35 1 95 -1
+ 35 95 94 -1
+ 1 0 96 -1
+ 1 96 95 -1
+ ]
+ coord DEF Cylinder2_Coord Coordinate {
+ point [
+ 0 1 -6
+ -1.17054 1 -5.88471
+ -1.07212 1 -5.3899
+ -1.05189 1 -5.39596
+ .02145 1 -5.49809
+ 1.09408 1 -5.38886
+ 2.1248 1 -5.07248
+ 3.07398 1 -4.56108
+ 3.90517 1 -3.87434
+ 4.5864 1 -3.03864
+ 5.0915 1 -2.08609
+ 5.40107 1 -1.0533
+ 5.5032 1 .02003
+ 5.39398 1 1.09267
+ 5.07759 1 2.12338
+ 4.5662 1 3.07257
+ 3.87945 1 3.90375
+ 3.04375 1 4.58498
+ 2.0912 1 5.09009
+ 1.05842 1 5.39966
+ -.01492 1 5.50179
+ -1.08755 1 5.39256
+ -2.11827 1 5.07617
+ -3.06746 1 4.56478
+ -3.89864 1 3.87804
+ -4.57987 1 3.04233
+ -5.08498 1 2.08979
+ -5.39454 1 1.057
+ -5.49668 1 -.01633
+ -5.38745 1 -1.08897
+ -5.07106 1 -2.11968
+ -4.55967 1 -3.06887
+ -3.87292 1 -3.90005
+ -3.03722 1 -4.58129
+ -2.08467 1 -5.08639
+ -2.2961 1 -5.54328
+ -3.33342 1 -4.98882
+ -4.24264 1 -4.24264
+ -4.98882 1 -3.33342
+ -5.54328 1 -2.2961
+ -5.88471 1 -1.17054
+ -6 1 -0
+ -5.88471 1 1.17054
+ -5.54328 1 2.2961
+ -4.98882 1 3.33342
+ -4.24264 1 4.24264
+ -3.33342 1 4.98882
+ -2.2961 1 5.54328
+ -1.17054 1 5.88471
+ -0 1 6
+ 1.17054 1 5.88471
+ 2.2961 1 5.54328
+ 3.33342 1 4.98882
+ 4.24264 1 4.24264
+ 4.98882 1 3.33342
+ 5.54328 1 2.2961
+ 5.88471 1 1.17054
+ 6 1 0
+ 5.88471 1 -1.17054
+ 5.54328 1 -2.2961
+ 4.98882 1 -3.33342
+ 4.24264 1 -4.24264
+ 3.33342 1 -4.98882
+ 2.2961 1 -5.54328
+ 1.17054 1 -5.88471
+ 1.17054 -1 -5.88471
+ 2.2961 -1 -5.54328
+ 3.33342 -1 -4.98882
+ 4.24264 -1 -4.24264
+ 4.98882 -1 -3.33342
+ 5.54328 -1 -2.2961
+ 5.88471 -1 -1.17054
+ 6 -1 -0
+ 5.88471 -1 1.17054
+ 5.54328 -1 2.2961
+ 4.98882 -1 3.33342
+ 4.24264 -1 4.24264
+ 3.33342 -1 4.98882
+ 2.2961 -1 5.54328
+ 1.17054 -1 5.88471
+ -0 -1 6
+ -1.17054 -1 5.88471
+ -2.2961 -1 5.54328
+ -3.33342 -1 4.98882
+ -4.24264 -1 4.24264
+ -4.98882 -1 3.33342
+ -5.54328 -1 2.2961
+ -5.88471 -1 1.17054
+ -6 -1 -0
+ -5.88471 -1 -1.17054
+ -5.54328 -1 -2.2961
+ -4.98882 -1 -3.33342
+ -4.24264 -1 -4.24264
+ -3.33342 -1 -4.98882
+ -2.2961 -1 -5.54328
+ -1.17054 -1 -5.88471
+ 0 -1 -6
+ 0 -1 -5.50037
+ -1.05842 -1 -5.39966
+ -2.0912 -1 -5.09009
+ -3.04375 -1 -4.58498
+ -3.87945 -1 -3.90375
+ -4.5662 -1 -3.07257
+ -5.07759 -1 -2.12338
+ -5.39398 -1 -1.09267
+ -5.5032 -1 -.02003
+ -5.40107 -1 1.0533
+ -5.0915 -1 2.08609
+ -4.5864 -1 3.03864
+ -3.90517 -1 3.87434
+ -3.07398 -1 4.56108
+ -2.1248 -1 5.07248
+ -1.09408 -1 5.38886
+ -.02145 -1 5.49809
+ 1.05189 -1 5.39596
+ 2.08467 -1 5.08639
+ 3.03722 -1 4.58129
+ 3.87292 -1 3.90005
+ 4.55967 -1 3.06887
+ 5.07106 -1 2.11968
+ 5.38745 -1 1.08897
+ 5.49668 -1 .01633
+ 5.39454 -1 -1.057
+ 5.08498 -1 -2.08979
+ 4.57987 -1 -3.04233
+ 3.89864 -1 -3.87804
+ 3.06746 -1 -4.56478
+ 2.11827 -1 -5.07617
+ 1.08755 -1 -5.39256
+ .01492 -1 -5.50179
+ ]
+ }
+ }
+ }
+ ]
+ }
+ DEF dad_Cylinder3 Transform {
+ rotation 0 0 1 1.571
+ scale 1.1 1.5 1.1
+ children [
+ DEF Cylinder3 Shape {
+ appearance Appearance {
+ material USE Green
+ }
+ geometry DEF Cylinder3_Geo IndexedFaceSet {
+ creaseAngle 0.524
+ coordIndex [
+ 0 1 2 -1
+ 0 2 3 -1
+ 4 5 1 -1
+ 4 1 0 -1
+ 6 7 5 -1
+ 6 5 4 -1
+ 8 9 7 -1
+ 8 7 6 -1
+ 10 11 9 -1
+ 10 9 8 -1
+ 12 13 11 -1
+ 12 11 10 -1
+ 14 15 13 -1
+ 14 13 12 -1
+ 16 17 15 -1
+ 16 15 14 -1
+ 18 19 17 -1
+ 18 17 16 -1
+ 20 21 19 -1
+ 20 19 18 -1
+ 22 23 21 -1
+ 22 21 20 -1
+ 24 25 23 -1
+ 24 23 22 -1
+ 26 27 25 -1
+ 26 25 24 -1
+ 28 29 27 -1
+ 28 27 26 -1
+ 30 31 29 -1
+ 30 29 28 -1
+ 32 33 31 -1
+ 32 31 30 -1
+ 34 35 33 -1
+ 34 33 32 -1
+ 36 37 35 -1
+ 36 35 34 -1
+ 38 39 37 -1
+ 38 37 36 -1
+ 40 41 39 -1
+ 40 39 38 -1
+ 42 43 41 -1
+ 42 41 40 -1
+ 44 45 43 -1
+ 44 43 42 -1
+ 46 47 45 -1
+ 46 45 44 -1
+ 48 49 47 -1
+ 48 47 46 -1
+ 50 51 49 -1
+ 50 49 48 -1
+ 52 53 51 -1
+ 52 51 50 -1
+ 54 55 53 -1
+ 54 53 52 -1
+ 56 57 55 -1
+ 56 55 54 -1
+ 58 59 57 -1
+ 58 57 56 -1
+ 60 61 59 -1
+ 60 59 58 -1
+ 62 63 61 -1
+ 62 61 60 -1
+ 3 2 63 -1
+ 3 63 62 -1
+ ]
+ coord DEF Cylinder3_Coord Coordinate {
+ point [
+ 1.073 -1.00635 -5.39432
+ 1.073 .99367 -5.39432
+ 0 .98997 -5.5
+ 0 -1.01006 -5.5
+ 2.10476 -1.0024 -5.08134
+ 2.10476 .99763 -5.08134
+ 3.05564 -.99836 -4.57308
+ 3.05564 1.00167 -4.57308
+ 3.88909 -.99438 -3.88909
+ 3.88909 1.00565 -3.88909
+ 4.57308 -.99061 -3.05564
+ 4.57308 1.00941 -3.05564
+ 5.08134 -.98721 -2.10476
+ 5.08134 1.01282 -2.10476
+ 5.39432 -.9843 -1.073
+ 5.39432 1.01573 -1.073
+ 5.5 -.98199 -0
+ 5.5 1.01803 -0
+ 5.39432 -.98038 1.073
+ 5.39432 1.01965 1.073
+ 5.08134 -.97952 2.10476
+ 5.08134 1.02051 2.10476
+ 4.57308 -.97945 3.05564
+ 4.57308 1.02058 3.05564
+ 3.88909 -.98017 3.88909
+ 3.88909 1.01986 3.88909
+ 3.05564 -.98165 4.57308
+ 3.05564 1.01838 4.57308
+ 2.10476 -.98383 5.08134
+ 2.10476 1.01619 5.08134
+ 1.073 -.98664 5.39432
+ 1.073 1.01338 5.39432
+ -0 -.98997 5.5
+ -0 1.01006 5.5
+ -1.073 -.99367 5.39432
+ -1.073 1.00635 5.39432
+ -2.10476 -.99763 5.08134
+ -2.10476 1.0024 5.08134
+ -3.05564 -1.00167 4.57308
+ -3.05564 .99836 4.57308
+ -3.88909 -1.00565 3.88909
+ -3.88909 .99438 3.88909
+ -4.57308 -1.00941 3.05564
+ -4.57308 .99061 3.05564
+ -5.08134 -1.01282 2.10476
+ -5.08134 .98721 2.10476
+ -5.39432 -1.01573 1.073
+ -5.39432 .9843 1.073
+ -5.5 -1.01803 -0
+ -5.5 .98199 -0
+ -5.39432 -1.01965 -1.073
+ -5.39432 .98038 -1.073
+ -5.08134 -1.02051 -2.10476
+ -5.08134 .97952 -2.10476
+ -4.57308 -1.02058 -3.05564
+ -4.57308 .97945 -3.05564
+ -3.88909 -1.01986 -3.88909
+ -3.88909 .98017 -3.88909
+ -3.05564 -1.01838 -4.57308
+ -3.05564 .98165 -4.57308
+ -2.10476 -1.01619 -5.08134
+ -2.10476 .98383 -5.08134
+ -1.073 -1.01338 -5.39432
+ -1.073 .98664 -5.39432
+ ]
+ }
+ }
+ }
+ ]
+ }
+ DEF dad_Box10 Transform {
+ translation 0 2 -8.4
+ children [
+ DEF Box10 Shape {
+ appearance Appearance {
+ material USE Green
+ }
+ geometry DEF GeoBox10 Box {
+ size .25 1 4.25
+ }
+ }
+ ]
+ }
+ DEF dad_Cylinder5 Transform {
+ translation 0 0 -8.4
+ rotation 1 0 0 1.571
+ children [
+ DEF Cylinder5 Shape {
+ appearance Appearance {
+ material USE Green
+ }
+ geometry DEF GeoCylinder5 Cylinder {
+ height 4.250
+ radius 2.000
+ }
+ }
+ ]
+ }
+ ]
+ }
+ DEF dad_lab_posttop Transform {
+ translation 0 0 -11
+ rotation -1 0 0 1.571
+ children [
+ DEF alpha_posttop Shape {
+ appearance Appearance {
+ material USE Red
+ }
+ geometry DEF GeoCylinder6 Cylinder {
+ height 1.000
+ radius 2.000
+ }
+ }
+ ]
+ }
+ DEF dad_alpha_arm Transform {
+ translation 0 -20.5 -5
+ children [
+ DEF alpha_arm Shape {
+ appearance Appearance {
+ material USE Red
+ }
+ geometry DEF GeoBox7 Box {
+ size 4 1 13
+ }
+ }
+ ]
+ }
+ DEF dad_gamma_arm1 Transform {
+ translation -6 -20.5 0
+ children [
+ DEF gamma_arm1 Shape {
+ appearance Appearance {
+ material USE Red
+ }
+ geometry DEF GeoBox18 Box {
+ size 1 2 .25
+ }
+ }
+ ]
+ }
+ DEF dad_Box12 Transform {
+ translation 0 2 -11
+ children [
+ DEF Box12 Shape {
+ appearance Appearance {
+ material USE Red
+ }
+ geometry DEF GeoBox12 Box {
+ size .25 1 1
+ }
+ }
+ ]
+ }
+ ]
+ }
+ DEF dad_gamma_frame Transform {
+ rotation 0 1 0 .349
+ children [
+ DEF dad_lab_posttop0 Transform {
+ translation 0 0 -12.5
+ rotation -1 0 0 1.571
+ children [
+ DEF gamma_posttop Shape {
+ appearance Appearance {
+ material DEF Blue Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor 0 0 .62745
+ }
+ }
+ geometry DEF GeoCylinder4 Cylinder {
+ height 1.000
+ radius 2.000
+ }
+ }
+ ]
+ }
+ DEF dad_lab_post0 Transform {
+ translation 0 -11 -12.5
+ children [
+ DEF gamma_post Shape {
+ appearance Appearance {
+ material USE Blue
+ }
+ geometry DEF GeoBox5 Box {
+ size 4 22 1
+ }
+ }
+ ]
+ }
+ DEF dad_gamma_base Transform {
+ translation 0 -22.5 0
+ children [
+ DEF gamma_base Shape {
+ appearance Appearance {
+ material USE Blue
+ }
+ geometry DEF GeoCylinder3 Cylinder {
+ height 2.000
+ radius 8.000
+ }
+ }
+ ]
+ }
+ DEF dad_gamma_arm Transform {
+ translation 0 -22.5 -6.5
+ children [
+ DEF gamma_arm Shape {
+ appearance Appearance {
+ material USE Blue
+ }
+ geometry DEF GeoBox4 Box {
+ size 4 1 13
+ }
+ }
+ ]
+ }
+ DEF dad_delta_frame Transform {
+ rotation 0 0 1 -.524
+ children [
+ DEF dad_delta_mount Transform {
+ translation 0 0 -15
+ rotation -1 0 0 1.571
+ children [
+ DEF delta_mount Shape {
+ appearance Appearance {
+ material DEF Yellow Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .83529 .83529 0
+ }
+ }
+ geometry DEF GeoCylinder2 Cylinder {
+ height 4.000
+ radius 2.000
+ }
+ }
+ ]
+ }
+ DEF dad_delta_arm Transform {
+ translation -10 0 -15
+ children [
+ DEF delta_arm Shape {
+ appearance Appearance {
+ material USE Yellow
+ }
+ geometry DEF GeoBox8 Box {
+ size 20 3 1
+ }
+ }
+ ]
+ }
+ DEF dad_delta_arm0 Transform {
+ translation -20 0 -6.5
+ children [
+ DEF delta_arm0 Shape {
+ appearance Appearance {
+ material USE Yellow
+ }
+ geometry DEF GeoBox6 Box {
+ size 1 3 18
+ }
+ }
+ ]
+ }
+ DEF dad_Cylinder14 Transform {
+ translation -18 0 0
+ rotation .577 -.577 .577 2.094
+ children [
+ DEF Cylinder14 Shape {
+ appearance Appearance {
+ material USE Yellow
+ }
+ geometry DEF GeoCylinder14 Cylinder {
+ height 3.000
+ radius 1.000
+ }
+ }
+ ]
+ }
+ DEF dad_detector_beam Transform {
+ translation -10 0 0
+ rotation 0 0 1 1.571
+ children [
+ DEF detector_beam Shape {
+ appearance Appearance {
+ material USE White
+ }
+ geometry DEF GeoCylinder21 Cylinder {
+ height 20.000
+ radius 0.050
+ }
+ }
+ ]
+ }
+ DEF dad_Box13 Transform {
+ translation 0 2 -15
+ children [
+ DEF Box13 Shape {
+ appearance Appearance {
+ material USE Yellow
+ }
+ geometry DEF GeoBox13 Box {
+ size .25 1 4
+ }
+ }
+ ]
+ }
+ ]
+ }
+ DEF dad_gamma_arm0 Transform {
+ translation -8 -22.5 0
+ children [
+ DEF gamma_arm0 Shape {
+ appearance Appearance {
+ material USE Blue
+ }
+ geometry DEF GeoBox17 Box {
+ size 1 2 .25
+ }
+ }
+ ]
+ }
+ DEF dad_Box14 Transform {
+ translation 0 2 -12.5
+ children [
+ DEF Box14 Shape {
+ appearance Appearance {
+ material USE Blue
+ }
+ geometry DEF GeoBox14 Box {
+ size .25 1 1
+ }
+ }
+ ]
+ }
+ ]
+ }
+ DEF dad_gamma_arm2 Transform {
+ translation -9 -24.5 0
+ children [
+ DEF gamma_arm2 Shape {
+ appearance Appearance {
+ material DEF Black Material {
+ ambientIntensity 0.200
+ shininess 0.200
+ diffuseColor .20784 .20784 .20784
+ }
+ }
+ geometry DEF GeoBox19 Box {
+ size 1 2 .25
+ }
+ }
+ ]
+ }
+ DEF dad_gamma_base0 Transform {
+ translation 0 -24.5 0
+ rotation 0 1 0 .349
+ children [
+ DEF gamma_base0 Shape {
+ appearance Appearance {
+ material USE Black
+ }
+ geometry DEF GeoCylinder9 Cylinder {
+ height 2.000
+ radius 9.000
+ }
+ }
+ ]
+ }
+ DEF dad_floor Transform {
+ translation 0 -25.5 0
+ children [
+ DEF floor Shape {
+ appearance Appearance {
+ material USE Black
+ }
+ geometry DEF GeoBox16 Box {
+ size 60 .2 40
+ }
+ }
+ ]
+ }
+ DEF VP Viewpoint {
+ description "VP"
+ jump TRUE
+ fieldOfView 1.165
+ position 40 40 40
+ orientation -.754 .657 -0 1.001
+ }
+ ]
+}
diff --git a/script/__Lib/diffcalc-2.1/model/vrml_animator.py b/script/__Lib/diffcalc-2.1/model/vrml_animator.py
new file mode 100755
index 0000000..d283469
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/model/vrml_animator.py
@@ -0,0 +1,134 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+import sys
+import time
+import threading
+from math import pi
+import socket
+
+from pivy.coin import *
+from pivy.sogui import *
+
+
+PORT = 4567
+TORAD = pi / 180
+
+
+def connect_to_socket(host, port):
+ print "Connecting to %s on port %d" % (host, port)
+ connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ print "Connected"
+ connection.connect((host, port))
+ socketfile = connection.makefile('rw', 0)
+ return socketfile
+
+
+def serve_socket_connection(port):
+ print ("Serving connection on all interfaces on %s port %d" %
+ (socket.gethostname(), port))
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.bind((socket.gethostname(), port))
+ sock.listen(1)
+ time.sleep(1)
+ (connection, addr) = sock.accept()
+ print 'Connected from ', addr, ' accepted'
+ socket_file = connection.makefile('rw', 0) # no buffering
+ return socket_file
+
+
+def node_name(anglename):
+ return 'dad_' + anglename + '_frame'
+
+
+class SceneUpdatingThread(threading.Thread):
+
+ def __init__(self, scene, axisnames):
+ threading.Thread.__init__(self)
+ self.scene = scene
+
+ # Infer rotation axes based on initial orientation
+ self.rotation_axes = {}
+ self.axies_nodes = {}
+ for axisname in axisnames:
+ node = self.scene.getByName(node_name(axisname))
+ self.axies_nodes[axisname] = node
+ value = node.rotation.getValue()
+ self.rotation_axes[axisname] = value.getAxisAngle()[0]
+
+ def run(self):
+ socket_file = serve_socket_connection(PORT)
+
+ while True:
+ msg = socket_file.readline()
+ if msg == '':
+ print '***Socket closed'
+ socket_file = serve_socket_connection(PORT)
+ continue
+ print msg.strip()
+ d = eval(msg.strip()) # msg should be a dictionary representation
+ for axisname in d:
+ self.set_axis_rotation(axisname, d[axisname])
+
+ def set_axis_rotation(self, anglename, degrees):
+ nodename = node_name(anglename)
+ angle = degrees * TORAD
+ while angle < 0:
+ angle = 2 * pi + angle
+ node = self.scene.getByName(nodename)
+ getattr(node, 'rotation').setValue(
+ self.rotation_axes[anglename], angle)
+
+
+class Animator(object):
+
+ def __init__(self, filename, axisnames):
+ print "filename : " + filename
+ print " axes : " + ' '.join(axisnames)
+ # Create viewer
+ self.myWindow = SoGui.init(sys.argv[0]) # @UndefinedVariable
+ if self.myWindow is None: sys.exit(1)
+ viewer = SoGuiExaminerViewer(self.myWindow) # @UndefinedVariable
+ # load file into scene
+ so_input = SoInput() # @UndefinedVariable
+ so_input.openFile(filename)
+ self.scene = SoDB.readAll(so_input) # @UndefinedVariable
+ # Add scene to viewer
+ viewer.setSceneGraph(self.scene)
+ viewer.setTitle(' '.join(axisnames))
+ viewer.show()
+
+ self.start_update_scene_thread(axisnames)
+
+ def start_update_scene_thread(self, axisnames):
+ t = SceneUpdatingThread(self.scene, axisnames)
+ t.setDaemon(True)
+ t.start()
+
+ def show(self):
+ SoGui.show(self.myWindow) # @UndefinedVariable
+ SoGui.mainLoop() # @UndefinedVariable
+
+def main():
+ animator = Animator(sys.argv[1], sys.argv[2:])
+ animator.show()
+
+if __name__ == "__main__":
+ main()
+
diff --git a/script/__Lib/diffcalc-2.1/numjy/__init__.py b/script/__Lib/diffcalc-2.1/numjy/__init__.py
new file mode 100755
index 0000000..273b233
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/numjy/__init__.py
@@ -0,0 +1,36 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+try:
+ import Jama
+ from numjy import linalg
+ from numjy.jama_matrix_wrapper import matrix
+ JAMA = True
+except ImportError:
+ JAMA = False
+
+
+def hstack(list_of_column_matrices):
+ if not Jama:
+ raise Exception('Jama not available, use numpy directly')
+ ncol = len(list_of_column_matrices)
+ nrow = list_of_column_matrices[0].shape[0]
+ m = Jama.Matrix(nrow, ncol)
+ for c, column_matrix in enumerate(list_of_column_matrices):
+ m.setMatrix(0, nrow - 1, c, c, column_matrix.m)
+ return matrix(m)
diff --git a/script/__Lib/diffcalc-2.1/numjy/jama_matrix_wrapper.py b/script/__Lib/diffcalc-2.1/numjy/jama_matrix_wrapper.py
new file mode 100755
index 0000000..d5e7645
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/numjy/jama_matrix_wrapper.py
@@ -0,0 +1,120 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+import Jama
+
+
+class matrix(object):
+
+ def __init__(self, a):
+ if isinstance(a, Jama.Matrix):
+ self.m = a
+ elif isinstance(a, basestring):
+ l = []
+ for row in a.strip().split(';'):
+ l.append([float(element)
+ for element in row.replace(',', ' ').split()])
+ self.m = Jama.Matrix(l)
+ elif isinstance(a, matrix):
+ self.m = Jama.Matrix(a.m)
+ elif isinstance(a, (list, tuple)):
+ if isinstance(a[0], (list, tuple)):
+ # a is a list of lists (not rigorous test!)
+ self.m = Jama.Matrix(a)
+ else:
+ # a is a row vector
+ self.m = Jama.Matrix([a])
+ else:
+ # give it a go
+ self.m = Jama.Matrix(a)
+
+ def __eq__(self, other):
+ nrow, ncol = self.shape
+ b = matrix(Jama.Matrix(nrow, ncol))
+ for i in range(nrow):
+ for j in range(ncol):
+ b[i, j] = self[i, j] == other[i, j]
+ return b
+
+ @property
+ def shape(self):
+ return self.m.getRowDimension(), self.m.getColumnDimension()
+
+ def __len__(self):
+ return self.m.getRowDimension()
+
+ def all(self): # @ReservedAssignment
+ for row in self.m.array:
+ if not all(row):
+ return False
+ return True
+
+ def tolist(self):
+ l = []
+ nrow, ncol = self.shape
+ for i in range(nrow):
+ row = []
+ for j in range(ncol):
+ row.append(self[i, j])
+ l.append(row)
+ return l
+
+ def sum(self): # @ReservedAssignment
+ return sum(sum(row) for row in self.m.array)
+
+ @property
+ def I(self):
+ return matrix(self.m.inverse())
+
+ @property
+ def T(self):
+ return matrix(self.m.transpose())
+
+ def _scaler(self, scaler):
+ return Jama.Matrix(self.shape[0], self.shape[1], scaler)
+
+ def __add__(self, other):
+ v = other.m if isinstance(other, matrix) else self._scaler(other)
+ return matrix(self.m.plus(v))
+
+ def __sub__(self, other):
+ v = other.m if isinstance(other, matrix) else self._scaler(other)
+ return matrix(self.m.minus(v))
+
+ def __mul__(self, other):
+ return matrix(self.m.times(other.m if isinstance(other, matrix) else
+ other))
+
+ def __div__(self, other):
+ # dividend = other.I if isinstance(other, matrix) else 1. /float(other)
+ return self.__mul__(1. / float(other))
+
+ def __getitem__(self, key):
+ i, j = key
+ return self.m.get(i, j)
+
+ def __setitem__(self, key, value):
+ i, j = key
+ self.m.set(i, j, value)
+
+ def __str__(self):
+ insides = [' '.join([str(el) for el in row]) for row in self.tolist()]
+ return '[[' + ']\n ['.join(insides) + ']]'
+
+ def __repr__(self):
+ return 'matrix(' + '\n '.join(self.__str__().split('\n')) + ')'
diff --git a/script/__Lib/diffcalc-2.1/numjy/linalg.py b/script/__Lib/diffcalc-2.1/numjy/linalg.py
new file mode 100755
index 0000000..218d824
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/numjy/linalg.py
@@ -0,0 +1,20 @@
+###
+# Copyright 2008-2011 Diamond Light Source Ltd.
+# This file is part of Diffcalc.
+#
+# Diffcalc 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.
+#
+# Diffcalc 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 Diffcalc. If not, see .
+###
+
+def norm(mat):
+ return mat.m.normF()
diff --git a/script/__Lib/diffcalc-2.1/setup.py b/script/__Lib/diffcalc-2.1/setup.py
new file mode 100755
index 0000000..0d549da
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/setup.py
@@ -0,0 +1,31 @@
+from setuptools import setup, find_packages
+
+setup(
+ name='diffcalc',
+ version='2.1',
+
+ description='A diffraction condition calculator for X-ray or neutron diffractometer control.',
+ long_description=open('README.rst').read(),
+ url='https://github.com/DiamondLightSource/diffcalc',
+
+ author='Rob Walton',
+ author_email='rob.walton@diamond.ac.uk',
+
+ license='GNU',
+
+ packages=find_packages(exclude=['docs']),
+
+ install_requires=[
+ 'numpy',
+ 'ipython',
+ 'pytest',
+ 'pytest-xdist',
+ 'nose'
+ ],
+
+ entry_points={
+ 'console_scripts': [
+ 'diffcalc=diffcmd.diffcalc_launcher:main',
+ ],
+ },
+)
\ No newline at end of file
diff --git a/script/__Lib/diffcalc-2.1/simplejson/__init__.py b/script/__Lib/diffcalc-2.1/simplejson/__init__.py
new file mode 100755
index 0000000..fe2bd5a
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/simplejson/__init__.py
@@ -0,0 +1,510 @@
+r"""JSON (JavaScript Object Notation) is a subset of
+JavaScript syntax (ECMA-262 3rd edition) used as a lightweight data
+interchange format.
+
+:mod:`simplejson` exposes an API familiar to users of the standard library
+:mod:`marshal` and :mod:`pickle` modules. It is the externally maintained
+version of the :mod:`json` library contained in Python 2.6, but maintains
+compatibility with Python 2.4 and Python 2.5 and (currently) has
+significant performance advantages, even without using the optional C
+extension for speedups.
+
+Encoding basic Python object hierarchies::
+
+ >>> import simplejson as json
+ >>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
+ '["foo", {"bar": ["baz", null, 1.0, 2]}]'
+ >>> print json.dumps("\"foo\bar")
+ "\"foo\bar"
+ >>> print json.dumps(u'\u1234')
+ "\u1234"
+ >>> print json.dumps('\\')
+ "\\"
+ >>> print json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True)
+ {"a": 0, "b": 0, "c": 0}
+ >>> from StringIO import StringIO
+ >>> io = StringIO()
+ >>> json.dump(['streaming API'], io)
+ >>> io.getvalue()
+ '["streaming API"]'
+
+Compact encoding::
+
+ >>> import simplejson as json
+ >>> json.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
+ '[1,2,3,{"4":5,"6":7}]'
+
+Pretty printing::
+
+ >>> import simplejson as json
+ >>> s = json.dumps({'4': 5, '6': 7}, sort_keys=True, indent=' ')
+ >>> print '\n'.join([l.rstrip() for l in s.splitlines()])
+ {
+ "4": 5,
+ "6": 7
+ }
+
+Decoding JSON::
+
+ >>> import simplejson as json
+ >>> obj = [u'foo', {u'bar': [u'baz', None, 1.0, 2]}]
+ >>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]') == obj
+ True
+ >>> json.loads('"\\"foo\\bar"') == u'"foo\x08ar'
+ True
+ >>> from StringIO import StringIO
+ >>> io = StringIO('["streaming API"]')
+ >>> json.load(io)[0] == 'streaming API'
+ True
+
+Specializing JSON object decoding::
+
+ >>> import simplejson as json
+ >>> def as_complex(dct):
+ ... if '__complex__' in dct:
+ ... return complex(dct['real'], dct['imag'])
+ ... return dct
+ ...
+ >>> json.loads('{"__complex__": true, "real": 1, "imag": 2}',
+ ... object_hook=as_complex)
+ (1+2j)
+ >>> from decimal import Decimal
+ >>> json.loads('1.1', parse_float=Decimal) == Decimal('1.1')
+ True
+
+Specializing JSON object encoding::
+
+ >>> import simplejson as json
+ >>> def encode_complex(obj):
+ ... if isinstance(obj, complex):
+ ... return [obj.real, obj.imag]
+ ... raise TypeError(repr(o) + " is not JSON serializable")
+ ...
+ >>> json.dumps(2 + 1j, default=encode_complex)
+ '[2.0, 1.0]'
+ >>> json.JSONEncoder(default=encode_complex).encode(2 + 1j)
+ '[2.0, 1.0]'
+ >>> ''.join(json.JSONEncoder(default=encode_complex).iterencode(2 + 1j))
+ '[2.0, 1.0]'
+
+
+Using simplejson.tool from the shell to validate and pretty-print::
+
+ $ echo '{"json":"obj"}' | python -m simplejson.tool
+ {
+ "json": "obj"
+ }
+ $ echo '{ 1.2:3.4}' | python -m simplejson.tool
+ Expecting property name: line 1 column 2 (char 2)
+"""
+__version__ = '2.6.2'
+__all__ = [
+ 'dump', 'dumps', 'load', 'loads',
+ 'JSONDecoder', 'JSONDecodeError', 'JSONEncoder',
+ 'OrderedDict', 'simple_first',
+]
+
+__author__ = 'Bob Ippolito '
+
+from decimal import Decimal
+
+from decoder import JSONDecoder, JSONDecodeError
+from encoder import JSONEncoder, JSONEncoderForHTML
+def _import_OrderedDict():
+ import collections
+ try:
+ return collections.OrderedDict
+ except AttributeError:
+ import ordered_dict
+ return ordered_dict.OrderedDict
+OrderedDict = _import_OrderedDict()
+
+def _import_c_make_encoder():
+ try:
+ from simplejson._speedups import make_encoder
+ return make_encoder
+ except ImportError:
+ return None
+
+_default_encoder = JSONEncoder(
+ skipkeys=False,
+ ensure_ascii=True,
+ check_circular=True,
+ allow_nan=True,
+ indent=None,
+ separators=None,
+ encoding='utf-8',
+ default=None,
+ use_decimal=True,
+ namedtuple_as_object=True,
+ tuple_as_array=True,
+ bigint_as_string=False,
+ item_sort_key=None,
+)
+
+def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
+ allow_nan=True, cls=None, indent=None, separators=None,
+ encoding='utf-8', default=None, use_decimal=True,
+ namedtuple_as_object=True, tuple_as_array=True,
+ bigint_as_string=False, sort_keys=False, item_sort_key=None,
+ **kw):
+ """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
+ ``.write()``-supporting file-like object).
+
+ If ``skipkeys`` is true then ``dict`` keys that are not basic types
+ (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
+ will be skipped instead of raising a ``TypeError``.
+
+ If ``ensure_ascii`` is false, then the some chunks written to ``fp``
+ may be ``unicode`` instances, subject to normal Python ``str`` to
+ ``unicode`` coercion rules. Unless ``fp.write()`` explicitly
+ understands ``unicode`` (as in ``codecs.getwriter()``) this is likely
+ to cause an error.
+
+ If ``check_circular`` is false, then the circular reference check
+ for container types will be skipped and a circular reference will
+ result in an ``OverflowError`` (or worse).
+
+ If ``allow_nan`` is false, then it will be a ``ValueError`` to
+ serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``)
+ in strict compliance of the JSON specification, instead of using the
+ JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
+
+ If *indent* is a string, then JSON array elements and object members
+ will be pretty-printed with a newline followed by that string repeated
+ for each level of nesting. ``None`` (the default) selects the most compact
+ representation without any newlines. For backwards compatibility with
+ versions of simplejson earlier than 2.1.0, an integer is also accepted
+ and is converted to a string with that many spaces.
+
+ If ``separators`` is an ``(item_separator, dict_separator)`` tuple
+ then it will be used instead of the default ``(', ', ': ')`` separators.
+ ``(',', ':')`` is the most compact JSON representation.
+
+ ``encoding`` is the character encoding for str instances, default is UTF-8.
+
+ ``default(obj)`` is a function that should return a serializable version
+ of obj or raise TypeError. The default simply raises TypeError.
+
+ If *use_decimal* is true (default: ``True``) then decimal.Decimal
+ will be natively serialized to JSON with full precision.
+
+ If *namedtuple_as_object* is true (default: ``True``),
+ :class:`tuple` subclasses with ``_asdict()`` methods will be encoded
+ as JSON objects.
+
+ If *tuple_as_array* is true (default: ``True``),
+ :class:`tuple` (and subclasses) will be encoded as JSON arrays.
+
+ If *bigint_as_string* is true (default: ``False``), ints 2**53 and higher
+ or lower than -2**53 will be encoded as strings. This is to avoid the
+ rounding that happens in Javascript otherwise. Note that this is still a
+ lossy operation that will not round-trip correctly and should be used
+ sparingly.
+
+ If specified, *item_sort_key* is a callable used to sort the items in
+ each dictionary. This is useful if you want to sort items other than
+ in alphabetical order by key. This option takes precedence over
+ *sort_keys*.
+
+ If *sort_keys* is true (default: ``False``), the output of dictionaries
+ will be sorted by item.
+
+ To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
+ ``.default()`` method to serialize additional types), specify it with
+ the ``cls`` kwarg.
+
+ """
+ # cached encoder
+ if (not skipkeys and ensure_ascii and
+ check_circular and allow_nan and
+ cls is None and indent is None and separators is None and
+ encoding == 'utf-8' and default is None and use_decimal
+ and namedtuple_as_object and tuple_as_array
+ and not bigint_as_string and not item_sort_key and not kw):
+ iterable = _default_encoder.iterencode(obj)
+ else:
+ if cls is None:
+ cls = JSONEncoder
+ iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
+ check_circular=check_circular, allow_nan=allow_nan, indent=indent,
+ separators=separators, encoding=encoding,
+ default=default, use_decimal=use_decimal,
+ namedtuple_as_object=namedtuple_as_object,
+ tuple_as_array=tuple_as_array,
+ bigint_as_string=bigint_as_string,
+ sort_keys=sort_keys,
+ item_sort_key=item_sort_key,
+ **kw).iterencode(obj)
+ # could accelerate with writelines in some versions of Python, at
+ # a debuggability cost
+ for chunk in iterable:
+ fp.write(chunk)
+
+
+def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
+ allow_nan=True, cls=None, indent=None, separators=None,
+ encoding='utf-8', default=None, use_decimal=True,
+ namedtuple_as_object=True, tuple_as_array=True,
+ bigint_as_string=False, sort_keys=False, item_sort_key=None,
+ **kw):
+ """Serialize ``obj`` to a JSON formatted ``str``.
+
+ If ``skipkeys`` is false then ``dict`` keys that are not basic types
+ (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
+ will be skipped instead of raising a ``TypeError``.
+
+ If ``ensure_ascii`` is false, then the return value will be a
+ ``unicode`` instance subject to normal Python ``str`` to ``unicode``
+ coercion rules instead of being escaped to an ASCII ``str``.
+
+ If ``check_circular`` is false, then the circular reference check
+ for container types will be skipped and a circular reference will
+ result in an ``OverflowError`` (or worse).
+
+ If ``allow_nan`` is false, then it will be a ``ValueError`` to
+ serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in
+ strict compliance of the JSON specification, instead of using the
+ JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
+
+ If ``indent`` is a string, then JSON array elements and object members
+ will be pretty-printed with a newline followed by that string repeated
+ for each level of nesting. ``None`` (the default) selects the most compact
+ representation without any newlines. For backwards compatibility with
+ versions of simplejson earlier than 2.1.0, an integer is also accepted
+ and is converted to a string with that many spaces.
+
+ If ``separators`` is an ``(item_separator, dict_separator)`` tuple
+ then it will be used instead of the default ``(', ', ': ')`` separators.
+ ``(',', ':')`` is the most compact JSON representation.
+
+ ``encoding`` is the character encoding for str instances, default is UTF-8.
+
+ ``default(obj)`` is a function that should return a serializable version
+ of obj or raise TypeError. The default simply raises TypeError.
+
+ If *use_decimal* is true (default: ``True``) then decimal.Decimal
+ will be natively serialized to JSON with full precision.
+
+ If *namedtuple_as_object* is true (default: ``True``),
+ :class:`tuple` subclasses with ``_asdict()`` methods will be encoded
+ as JSON objects.
+
+ If *tuple_as_array* is true (default: ``True``),
+ :class:`tuple` (and subclasses) will be encoded as JSON arrays.
+
+ If *bigint_as_string* is true (not the default), ints 2**53 and higher
+ or lower than -2**53 will be encoded as strings. This is to avoid the
+ rounding that happens in Javascript otherwise.
+
+ If specified, *item_sort_key* is a callable used to sort the items in
+ each dictionary. This is useful if you want to sort items other than
+ in alphabetical order by key. This option takes precendence over
+ *sort_keys*.
+
+ If *sort_keys* is true (default: ``False``), the output of dictionaries
+ will be sorted by item.
+
+ To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
+ ``.default()`` method to serialize additional types), specify it with
+ the ``cls`` kwarg.
+
+ """
+ # cached encoder
+ if (not skipkeys and ensure_ascii and
+ check_circular and allow_nan and
+ cls is None and indent is None and separators is None and
+ encoding == 'utf-8' and default is None and use_decimal
+ and namedtuple_as_object and tuple_as_array
+ and not bigint_as_string and not sort_keys
+ and not item_sort_key and not kw):
+ return _default_encoder.encode(obj)
+ if cls is None:
+ cls = JSONEncoder
+ return cls(
+ skipkeys=skipkeys, ensure_ascii=ensure_ascii,
+ check_circular=check_circular, allow_nan=allow_nan, indent=indent,
+ separators=separators, encoding=encoding, default=default,
+ use_decimal=use_decimal,
+ namedtuple_as_object=namedtuple_as_object,
+ tuple_as_array=tuple_as_array,
+ bigint_as_string=bigint_as_string,
+ sort_keys=sort_keys,
+ item_sort_key=item_sort_key,
+ **kw).encode(obj)
+
+
+_default_decoder = JSONDecoder(encoding=None, object_hook=None,
+ object_pairs_hook=None)
+
+
+def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None,
+ parse_int=None, parse_constant=None, object_pairs_hook=None,
+ use_decimal=False, namedtuple_as_object=True, tuple_as_array=True,
+ **kw):
+ """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
+ a JSON document) to a Python object.
+
+ *encoding* determines the encoding used to interpret any
+ :class:`str` objects decoded by this instance (``'utf-8'`` by
+ default). It has no effect when decoding :class:`unicode` objects.
+
+ Note that currently only encodings that are a superset of ASCII work,
+ strings of other encodings should be passed in as :class:`unicode`.
+
+ *object_hook*, if specified, will be called with the result of every
+ JSON object decoded and its return value will be used in place of the
+ given :class:`dict`. This can be used to provide custom
+ deserializations (e.g. to support JSON-RPC class hinting).
+
+ *object_pairs_hook* is an optional function that will be called with
+ the result of any object literal decode with an ordered list of pairs.
+ The return value of *object_pairs_hook* will be used instead of the
+ :class:`dict`. This feature can be used to implement custom decoders
+ that rely on the order that the key and value pairs are decoded (for
+ example, :func:`collections.OrderedDict` will remember the order of
+ insertion). If *object_hook* is also defined, the *object_pairs_hook*
+ takes priority.
+
+ *parse_float*, if specified, will be called with the string of every
+ JSON float to be decoded. By default, this is equivalent to
+ ``float(num_str)``. This can be used to use another datatype or parser
+ for JSON floats (e.g. :class:`decimal.Decimal`).
+
+ *parse_int*, if specified, will be called with the string of every
+ JSON int to be decoded. By default, this is equivalent to
+ ``int(num_str)``. This can be used to use another datatype or parser
+ for JSON integers (e.g. :class:`float`).
+
+ *parse_constant*, if specified, will be called with one of the
+ following strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``. This
+ can be used to raise an exception if invalid JSON numbers are
+ encountered.
+
+ If *use_decimal* is true (default: ``False``) then it implies
+ parse_float=decimal.Decimal for parity with ``dump``.
+
+ To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
+ kwarg.
+
+ """
+ return loads(fp.read(),
+ encoding=encoding, cls=cls, object_hook=object_hook,
+ parse_float=parse_float, parse_int=parse_int,
+ parse_constant=parse_constant, object_pairs_hook=object_pairs_hook,
+ use_decimal=use_decimal, **kw)
+
+
+def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None,
+ parse_int=None, parse_constant=None, object_pairs_hook=None,
+ use_decimal=False, **kw):
+ """Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON
+ document) to a Python object.
+
+ *encoding* determines the encoding used to interpret any
+ :class:`str` objects decoded by this instance (``'utf-8'`` by
+ default). It has no effect when decoding :class:`unicode` objects.
+
+ Note that currently only encodings that are a superset of ASCII work,
+ strings of other encodings should be passed in as :class:`unicode`.
+
+ *object_hook*, if specified, will be called with the result of every
+ JSON object decoded and its return value will be used in place of the
+ given :class:`dict`. This can be used to provide custom
+ deserializations (e.g. to support JSON-RPC class hinting).
+
+ *object_pairs_hook* is an optional function that will be called with
+ the result of any object literal decode with an ordered list of pairs.
+ The return value of *object_pairs_hook* will be used instead of the
+ :class:`dict`. This feature can be used to implement custom decoders
+ that rely on the order that the key and value pairs are decoded (for
+ example, :func:`collections.OrderedDict` will remember the order of
+ insertion). If *object_hook* is also defined, the *object_pairs_hook*
+ takes priority.
+
+ *parse_float*, if specified, will be called with the string of every
+ JSON float to be decoded. By default, this is equivalent to
+ ``float(num_str)``. This can be used to use another datatype or parser
+ for JSON floats (e.g. :class:`decimal.Decimal`).
+
+ *parse_int*, if specified, will be called with the string of every
+ JSON int to be decoded. By default, this is equivalent to
+ ``int(num_str)``. This can be used to use another datatype or parser
+ for JSON integers (e.g. :class:`float`).
+
+ *parse_constant*, if specified, will be called with one of the
+ following strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``. This
+ can be used to raise an exception if invalid JSON numbers are
+ encountered.
+
+ If *use_decimal* is true (default: ``False``) then it implies
+ parse_float=decimal.Decimal for parity with ``dump``.
+
+ To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
+ kwarg.
+
+ """
+ if (cls is None and encoding is None and object_hook is None and
+ parse_int is None and parse_float is None and
+ parse_constant is None and object_pairs_hook is None
+ and not use_decimal and not kw):
+ return _default_decoder.decode(s)
+ if cls is None:
+ cls = JSONDecoder
+ if object_hook is not None:
+ kw['object_hook'] = object_hook
+ if object_pairs_hook is not None:
+ kw['object_pairs_hook'] = object_pairs_hook
+ if parse_float is not None:
+ kw['parse_float'] = parse_float
+ if parse_int is not None:
+ kw['parse_int'] = parse_int
+ if parse_constant is not None:
+ kw['parse_constant'] = parse_constant
+ if use_decimal:
+ if parse_float is not None:
+ raise TypeError("use_decimal=True implies parse_float=Decimal")
+ kw['parse_float'] = Decimal
+ return cls(encoding=encoding, **kw).decode(s)
+
+
+def _toggle_speedups(enabled):
+ import simplejson.decoder as dec
+ import simplejson.encoder as enc
+ import simplejson.scanner as scan
+ c_make_encoder = _import_c_make_encoder()
+ if enabled:
+ dec.scanstring = dec.c_scanstring or dec.py_scanstring
+ enc.c_make_encoder = c_make_encoder
+ enc.encode_basestring_ascii = (enc.c_encode_basestring_ascii or
+ enc.py_encode_basestring_ascii)
+ scan.make_scanner = scan.c_make_scanner or scan.py_make_scanner
+ else:
+ dec.scanstring = dec.py_scanstring
+ enc.c_make_encoder = None
+ enc.encode_basestring_ascii = enc.py_encode_basestring_ascii
+ scan.make_scanner = scan.py_make_scanner
+ dec.make_scanner = scan.make_scanner
+ global _default_decoder
+ _default_decoder = JSONDecoder(
+ encoding=None,
+ object_hook=None,
+ object_pairs_hook=None,
+ )
+ global _default_encoder
+ _default_encoder = JSONEncoder(
+ skipkeys=False,
+ ensure_ascii=True,
+ check_circular=True,
+ allow_nan=True,
+ indent=None,
+ separators=None,
+ encoding='utf-8',
+ default=None,
+ )
+
+def simple_first(kv):
+ """Helper function to pass to item_sort_key to sort simple
+ elements to the top, then container elements.
+ """
+ return (isinstance(kv[1], (list, dict, tuple)), kv[0])
diff --git a/script/__Lib/diffcalc-2.1/simplejson/_speedups.c b/script/__Lib/diffcalc-2.1/simplejson/_speedups.c
new file mode 100755
index 0000000..be68b2d
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/simplejson/_speedups.c
@@ -0,0 +1,2745 @@
+#include "Python.h"
+#include "structmember.h"
+#if PY_VERSION_HEX < 0x02070000 && !defined(PyOS_string_to_double)
+#define PyOS_string_to_double json_PyOS_string_to_double
+static double
+json_PyOS_string_to_double(const char *s, char **endptr, PyObject *overflow_exception);
+static double
+json_PyOS_string_to_double(const char *s, char **endptr, PyObject *overflow_exception) {
+ double x;
+ assert(endptr == NULL);
+ assert(overflow_exception == NULL);
+ PyFPE_START_PROTECT("json_PyOS_string_to_double", return -1.0;)
+ x = PyOS_ascii_atof(s);
+ PyFPE_END_PROTECT(x)
+ return x;
+}
+#endif
+#if PY_VERSION_HEX < 0x02060000 && !defined(Py_TYPE)
+#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
+#endif
+#if PY_VERSION_HEX < 0x02060000 && !defined(Py_SIZE)
+#define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size)
+#endif
+#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
+typedef int Py_ssize_t;
+#define PY_SSIZE_T_MAX INT_MAX
+#define PY_SSIZE_T_MIN INT_MIN
+#define PyInt_FromSsize_t PyInt_FromLong
+#define PyInt_AsSsize_t PyInt_AsLong
+#endif
+#ifndef Py_IS_FINITE
+#define Py_IS_FINITE(X) (!Py_IS_INFINITY(X) && !Py_IS_NAN(X))
+#endif
+
+#ifdef __GNUC__
+#define UNUSED __attribute__((__unused__))
+#else
+#define UNUSED
+#endif
+
+#define DEFAULT_ENCODING "utf-8"
+
+#define PyScanner_Check(op) PyObject_TypeCheck(op, &PyScannerType)
+#define PyScanner_CheckExact(op) (Py_TYPE(op) == &PyScannerType)
+#define PyEncoder_Check(op) PyObject_TypeCheck(op, &PyEncoderType)
+#define PyEncoder_CheckExact(op) (Py_TYPE(op) == &PyEncoderType)
+
+static PyTypeObject PyScannerType;
+static PyTypeObject PyEncoderType;
+
+typedef struct _PyScannerObject {
+ PyObject_HEAD
+ PyObject *encoding;
+ PyObject *strict;
+ PyObject *object_hook;
+ PyObject *pairs_hook;
+ PyObject *parse_float;
+ PyObject *parse_int;
+ PyObject *parse_constant;
+ PyObject *memo;
+} PyScannerObject;
+
+static PyMemberDef scanner_members[] = {
+ {"encoding", T_OBJECT, offsetof(PyScannerObject, encoding), READONLY, "encoding"},
+ {"strict", T_OBJECT, offsetof(PyScannerObject, strict), READONLY, "strict"},
+ {"object_hook", T_OBJECT, offsetof(PyScannerObject, object_hook), READONLY, "object_hook"},
+ {"object_pairs_hook", T_OBJECT, offsetof(PyScannerObject, pairs_hook), READONLY, "object_pairs_hook"},
+ {"parse_float", T_OBJECT, offsetof(PyScannerObject, parse_float), READONLY, "parse_float"},
+ {"parse_int", T_OBJECT, offsetof(PyScannerObject, parse_int), READONLY, "parse_int"},
+ {"parse_constant", T_OBJECT, offsetof(PyScannerObject, parse_constant), READONLY, "parse_constant"},
+ {NULL}
+};
+
+typedef struct _PyEncoderObject {
+ PyObject_HEAD
+ PyObject *markers;
+ PyObject *defaultfn;
+ PyObject *encoder;
+ PyObject *indent;
+ PyObject *key_separator;
+ PyObject *item_separator;
+ PyObject *sort_keys;
+ PyObject *skipkeys;
+ PyObject *key_memo;
+ PyObject *Decimal;
+ int fast_encode;
+ int allow_nan;
+ int use_decimal;
+ int namedtuple_as_object;
+ int tuple_as_array;
+ int bigint_as_string;
+ PyObject *item_sort_key;
+} PyEncoderObject;
+
+static PyMemberDef encoder_members[] = {
+ {"markers", T_OBJECT, offsetof(PyEncoderObject, markers), READONLY, "markers"},
+ {"default", T_OBJECT, offsetof(PyEncoderObject, defaultfn), READONLY, "default"},
+ {"encoder", T_OBJECT, offsetof(PyEncoderObject, encoder), READONLY, "encoder"},
+ {"indent", T_OBJECT, offsetof(PyEncoderObject, indent), READONLY, "indent"},
+ {"key_separator", T_OBJECT, offsetof(PyEncoderObject, key_separator), READONLY, "key_separator"},
+ {"item_separator", T_OBJECT, offsetof(PyEncoderObject, item_separator), READONLY, "item_separator"},
+ {"sort_keys", T_OBJECT, offsetof(PyEncoderObject, sort_keys), READONLY, "sort_keys"},
+ {"skipkeys", T_OBJECT, offsetof(PyEncoderObject, skipkeys), READONLY, "skipkeys"},
+ {"key_memo", T_OBJECT, offsetof(PyEncoderObject, key_memo), READONLY, "key_memo"},
+ {"item_sort_key", T_OBJECT, offsetof(PyEncoderObject, item_sort_key), READONLY, "item_sort_key"},
+ {NULL}
+};
+
+static PyObject *
+maybe_quote_bigint(PyObject *encoded, PyObject *obj);
+
+static Py_ssize_t
+ascii_escape_char(Py_UNICODE c, char *output, Py_ssize_t chars);
+static PyObject *
+ascii_escape_unicode(PyObject *pystr);
+static PyObject *
+ascii_escape_str(PyObject *pystr);
+static PyObject *
+py_encode_basestring_ascii(PyObject* self UNUSED, PyObject *pystr);
+void init_speedups(void);
+static PyObject *
+scan_once_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr);
+static PyObject *
+scan_once_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr);
+static PyObject *
+_build_rval_index_tuple(PyObject *rval, Py_ssize_t idx);
+static PyObject *
+scanner_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
+static int
+scanner_init(PyObject *self, PyObject *args, PyObject *kwds);
+static void
+scanner_dealloc(PyObject *self);
+static int
+scanner_clear(PyObject *self);
+static PyObject *
+encoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
+static int
+encoder_init(PyObject *self, PyObject *args, PyObject *kwds);
+static void
+encoder_dealloc(PyObject *self);
+static int
+encoder_clear(PyObject *self);
+static int
+encoder_listencode_list(PyEncoderObject *s, PyObject *rval, PyObject *seq, Py_ssize_t indent_level);
+static int
+encoder_listencode_obj(PyEncoderObject *s, PyObject *rval, PyObject *obj, Py_ssize_t indent_level);
+static int
+encoder_listencode_dict(PyEncoderObject *s, PyObject *rval, PyObject *dct, Py_ssize_t indent_level);
+static PyObject *
+_encoded_const(PyObject *obj);
+static void
+raise_errmsg(char *msg, PyObject *s, Py_ssize_t end);
+static PyObject *
+encoder_encode_string(PyEncoderObject *s, PyObject *obj);
+static int
+_convertPyInt_AsSsize_t(PyObject *o, Py_ssize_t *size_ptr);
+static PyObject *
+_convertPyInt_FromSsize_t(Py_ssize_t *size_ptr);
+static PyObject *
+encoder_encode_float(PyEncoderObject *s, PyObject *obj);
+static int
+_is_namedtuple(PyObject *obj);
+
+#define S_CHAR(c) (c >= ' ' && c <= '~' && c != '\\' && c != '"')
+#define IS_WHITESPACE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\n') || ((c) == '\r'))
+
+#define MIN_EXPANSION 6
+#ifdef Py_UNICODE_WIDE
+#define MAX_EXPANSION (2 * MIN_EXPANSION)
+#else
+#define MAX_EXPANSION MIN_EXPANSION
+#endif
+
+static PyObject *
+maybe_quote_bigint(PyObject *encoded, PyObject *obj)
+{
+ static PyObject *big_long = NULL;
+ static PyObject *small_long = NULL;
+ if (big_long == NULL) {
+ big_long = PyLong_FromLongLong(1LL << 53);
+ if (big_long == NULL) {
+ Py_DECREF(encoded);
+ return NULL;
+ }
+ }
+ if (small_long == NULL) {
+ small_long = PyLong_FromLongLong(-1LL << 53);
+ if (small_long == NULL) {
+ Py_DECREF(encoded);
+ return NULL;
+ }
+ }
+ if (PyObject_RichCompareBool(obj, big_long, Py_GE) ||
+ PyObject_RichCompareBool(obj, small_long, Py_LE)) {
+ PyObject* quoted = PyString_FromFormat("\"%s\"",
+ PyString_AsString(encoded));
+ Py_DECREF(encoded);
+ encoded = quoted;
+ }
+ return encoded;
+}
+
+static int
+_is_namedtuple(PyObject *obj)
+{
+ int rval = 0;
+ PyObject *_asdict = PyObject_GetAttrString(obj, "_asdict");
+ if (_asdict == NULL) {
+ PyErr_Clear();
+ return 0;
+ }
+ rval = PyCallable_Check(_asdict);
+ Py_DECREF(_asdict);
+ return rval;
+}
+
+static int
+_convertPyInt_AsSsize_t(PyObject *o, Py_ssize_t *size_ptr)
+{
+ /* PyObject to Py_ssize_t converter */
+ *size_ptr = PyInt_AsSsize_t(o);
+ if (*size_ptr == -1 && PyErr_Occurred())
+ return 0;
+ return 1;
+}
+
+static PyObject *
+_convertPyInt_FromSsize_t(Py_ssize_t *size_ptr)
+{
+ /* Py_ssize_t to PyObject converter */
+ return PyInt_FromSsize_t(*size_ptr);
+}
+
+static Py_ssize_t
+ascii_escape_char(Py_UNICODE c, char *output, Py_ssize_t chars)
+{
+ /* Escape unicode code point c to ASCII escape sequences
+ in char *output. output must have at least 12 bytes unused to
+ accommodate an escaped surrogate pair "\uXXXX\uXXXX" */
+ output[chars++] = '\\';
+ switch (c) {
+ case '\\': output[chars++] = (char)c; break;
+ case '"': output[chars++] = (char)c; break;
+ case '\b': output[chars++] = 'b'; break;
+ case '\f': output[chars++] = 'f'; break;
+ case '\n': output[chars++] = 'n'; break;
+ case '\r': output[chars++] = 'r'; break;
+ case '\t': output[chars++] = 't'; break;
+ default:
+#ifdef Py_UNICODE_WIDE
+ if (c >= 0x10000) {
+ /* UTF-16 surrogate pair */
+ Py_UNICODE v = c - 0x10000;
+ c = 0xd800 | ((v >> 10) & 0x3ff);
+ output[chars++] = 'u';
+ output[chars++] = "0123456789abcdef"[(c >> 12) & 0xf];
+ output[chars++] = "0123456789abcdef"[(c >> 8) & 0xf];
+ output[chars++] = "0123456789abcdef"[(c >> 4) & 0xf];
+ output[chars++] = "0123456789abcdef"[(c ) & 0xf];
+ c = 0xdc00 | (v & 0x3ff);
+ output[chars++] = '\\';
+ }
+#endif
+ output[chars++] = 'u';
+ output[chars++] = "0123456789abcdef"[(c >> 12) & 0xf];
+ output[chars++] = "0123456789abcdef"[(c >> 8) & 0xf];
+ output[chars++] = "0123456789abcdef"[(c >> 4) & 0xf];
+ output[chars++] = "0123456789abcdef"[(c ) & 0xf];
+ }
+ return chars;
+}
+
+static PyObject *
+ascii_escape_unicode(PyObject *pystr)
+{
+ /* Take a PyUnicode pystr and return a new ASCII-only escaped PyString */
+ Py_ssize_t i;
+ Py_ssize_t input_chars;
+ Py_ssize_t output_size;
+ Py_ssize_t max_output_size;
+ Py_ssize_t chars;
+ PyObject *rval;
+ char *output;
+ Py_UNICODE *input_unicode;
+
+ input_chars = PyUnicode_GET_SIZE(pystr);
+ input_unicode = PyUnicode_AS_UNICODE(pystr);
+
+ /* One char input can be up to 6 chars output, estimate 4 of these */
+ output_size = 2 + (MIN_EXPANSION * 4) + input_chars;
+ max_output_size = 2 + (input_chars * MAX_EXPANSION);
+ rval = PyString_FromStringAndSize(NULL, output_size);
+ if (rval == NULL) {
+ return NULL;
+ }
+ output = PyString_AS_STRING(rval);
+ chars = 0;
+ output[chars++] = '"';
+ for (i = 0; i < input_chars; i++) {
+ Py_UNICODE c = input_unicode[i];
+ if (S_CHAR(c)) {
+ output[chars++] = (char)c;
+ }
+ else {
+ chars = ascii_escape_char(c, output, chars);
+ }
+ if (output_size - chars < (1 + MAX_EXPANSION)) {
+ /* There's more than four, so let's resize by a lot */
+ Py_ssize_t new_output_size = output_size * 2;
+ /* This is an upper bound */
+ if (new_output_size > max_output_size) {
+ new_output_size = max_output_size;
+ }
+ /* Make sure that the output size changed before resizing */
+ if (new_output_size != output_size) {
+ output_size = new_output_size;
+ if (_PyString_Resize(&rval, output_size) == -1) {
+ return NULL;
+ }
+ output = PyString_AS_STRING(rval);
+ }
+ }
+ }
+ output[chars++] = '"';
+ if (_PyString_Resize(&rval, chars) == -1) {
+ return NULL;
+ }
+ return rval;
+}
+
+static PyObject *
+ascii_escape_str(PyObject *pystr)
+{
+ /* Take a PyString pystr and return a new ASCII-only escaped PyString */
+ Py_ssize_t i;
+ Py_ssize_t input_chars;
+ Py_ssize_t output_size;
+ Py_ssize_t chars;
+ PyObject *rval;
+ char *output;
+ char *input_str;
+
+ input_chars = PyString_GET_SIZE(pystr);
+ input_str = PyString_AS_STRING(pystr);
+
+ /* Fast path for a string that's already ASCII */
+ for (i = 0; i < input_chars; i++) {
+ Py_UNICODE c = (Py_UNICODE)(unsigned char)input_str[i];
+ if (!S_CHAR(c)) {
+ /* If we have to escape something, scan the string for unicode */
+ Py_ssize_t j;
+ for (j = i; j < input_chars; j++) {
+ c = (Py_UNICODE)(unsigned char)input_str[j];
+ if (c > 0x7f) {
+ /* We hit a non-ASCII character, bail to unicode mode */
+ PyObject *uni;
+ uni = PyUnicode_DecodeUTF8(input_str, input_chars, "strict");
+ if (uni == NULL) {
+ return NULL;
+ }
+ rval = ascii_escape_unicode(uni);
+ Py_DECREF(uni);
+ return rval;
+ }
+ }
+ break;
+ }
+ }
+
+ if (i == input_chars) {
+ /* Input is already ASCII */
+ output_size = 2 + input_chars;
+ }
+ else {
+ /* One char input can be up to 6 chars output, estimate 4 of these */
+ output_size = 2 + (MIN_EXPANSION * 4) + input_chars;
+ }
+ rval = PyString_FromStringAndSize(NULL, output_size);
+ if (rval == NULL) {
+ return NULL;
+ }
+ output = PyString_AS_STRING(rval);
+ output[0] = '"';
+
+ /* We know that everything up to i is ASCII already */
+ chars = i + 1;
+ memcpy(&output[1], input_str, i);
+
+ for (; i < input_chars; i++) {
+ Py_UNICODE c = (Py_UNICODE)(unsigned char)input_str[i];
+ if (S_CHAR(c)) {
+ output[chars++] = (char)c;
+ }
+ else {
+ chars = ascii_escape_char(c, output, chars);
+ }
+ /* An ASCII char can't possibly expand to a surrogate! */
+ if (output_size - chars < (1 + MIN_EXPANSION)) {
+ /* There's more than four, so let's resize by a lot */
+ output_size *= 2;
+ if (output_size > 2 + (input_chars * MIN_EXPANSION)) {
+ output_size = 2 + (input_chars * MIN_EXPANSION);
+ }
+ if (_PyString_Resize(&rval, output_size) == -1) {
+ return NULL;
+ }
+ output = PyString_AS_STRING(rval);
+ }
+ }
+ output[chars++] = '"';
+ if (_PyString_Resize(&rval, chars) == -1) {
+ return NULL;
+ }
+ return rval;
+}
+
+static void
+raise_errmsg(char *msg, PyObject *s, Py_ssize_t end)
+{
+ /* Use the Python function simplejson.decoder.errmsg to raise a nice
+ looking ValueError exception */
+ static PyObject *JSONDecodeError = NULL;
+ PyObject *exc;
+ if (JSONDecodeError == NULL) {
+ PyObject *decoder = PyImport_ImportModule("simplejson.decoder");
+ if (decoder == NULL)
+ return;
+ JSONDecodeError = PyObject_GetAttrString(decoder, "JSONDecodeError");
+ Py_DECREF(decoder);
+ if (JSONDecodeError == NULL)
+ return;
+ }
+ exc = PyObject_CallFunction(JSONDecodeError, "(zOO&)", msg, s, _convertPyInt_FromSsize_t, &end);
+ if (exc) {
+ PyErr_SetObject(JSONDecodeError, exc);
+ Py_DECREF(exc);
+ }
+}
+
+static PyObject *
+join_list_unicode(PyObject *lst)
+{
+ /* return u''.join(lst) */
+ static PyObject *joinfn = NULL;
+ if (joinfn == NULL) {
+ PyObject *ustr = PyUnicode_FromUnicode(NULL, 0);
+ if (ustr == NULL)
+ return NULL;
+
+ joinfn = PyObject_GetAttrString(ustr, "join");
+ Py_DECREF(ustr);
+ if (joinfn == NULL)
+ return NULL;
+ }
+ return PyObject_CallFunctionObjArgs(joinfn, lst, NULL);
+}
+
+static PyObject *
+join_list_string(PyObject *lst)
+{
+ /* return ''.join(lst) */
+ static PyObject *joinfn = NULL;
+ if (joinfn == NULL) {
+ PyObject *ustr = PyString_FromStringAndSize(NULL, 0);
+ if (ustr == NULL)
+ return NULL;
+
+ joinfn = PyObject_GetAttrString(ustr, "join");
+ Py_DECREF(ustr);
+ if (joinfn == NULL)
+ return NULL;
+ }
+ return PyObject_CallFunctionObjArgs(joinfn, lst, NULL);
+}
+
+static PyObject *
+_build_rval_index_tuple(PyObject *rval, Py_ssize_t idx) {
+ /* return (rval, idx) tuple, stealing reference to rval */
+ PyObject *tpl;
+ PyObject *pyidx;
+ /*
+ steal a reference to rval, returns (rval, idx)
+ */
+ if (rval == NULL) {
+ return NULL;
+ }
+ pyidx = PyInt_FromSsize_t(idx);
+ if (pyidx == NULL) {
+ Py_DECREF(rval);
+ return NULL;
+ }
+ tpl = PyTuple_New(2);
+ if (tpl == NULL) {
+ Py_DECREF(pyidx);
+ Py_DECREF(rval);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(tpl, 0, rval);
+ PyTuple_SET_ITEM(tpl, 1, pyidx);
+ return tpl;
+}
+
+#define APPEND_OLD_CHUNK \
+ if (chunk != NULL) { \
+ if (chunks == NULL) { \
+ chunks = PyList_New(0); \
+ if (chunks == NULL) { \
+ goto bail; \
+ } \
+ } \
+ if (PyList_Append(chunks, chunk)) { \
+ goto bail; \
+ } \
+ Py_CLEAR(chunk); \
+ }
+
+static PyObject *
+scanstring_str(PyObject *pystr, Py_ssize_t end, char *encoding, int strict, Py_ssize_t *next_end_ptr)
+{
+ /* Read the JSON string from PyString pystr.
+ end is the index of the first character after the quote.
+ encoding is the encoding of pystr (must be an ASCII superset)
+ if strict is zero then literal control characters are allowed
+ *next_end_ptr is a return-by-reference index of the character
+ after the end quote
+
+ Return value is a new PyString (if ASCII-only) or PyUnicode
+ */
+ PyObject *rval;
+ Py_ssize_t len = PyString_GET_SIZE(pystr);
+ Py_ssize_t begin = end - 1;
+ Py_ssize_t next = begin;
+ int has_unicode = 0;
+ char *buf = PyString_AS_STRING(pystr);
+ PyObject *chunks = NULL;
+ PyObject *chunk = NULL;
+
+ if (len == end) {
+ raise_errmsg("Unterminated string starting at", pystr, begin);
+ }
+ else if (end < 0 || len < end) {
+ PyErr_SetString(PyExc_ValueError, "end is out of bounds");
+ goto bail;
+ }
+ while (1) {
+ /* Find the end of the string or the next escape */
+ Py_UNICODE c = 0;
+ for (next = end; next < len; next++) {
+ c = (unsigned char)buf[next];
+ if (c == '"' || c == '\\') {
+ break;
+ }
+ else if (strict && c <= 0x1f) {
+ raise_errmsg("Invalid control character at", pystr, next);
+ goto bail;
+ }
+ else if (c > 0x7f) {
+ has_unicode = 1;
+ }
+ }
+ if (!(c == '"' || c == '\\')) {
+ raise_errmsg("Unterminated string starting at", pystr, begin);
+ goto bail;
+ }
+ /* Pick up this chunk if it's not zero length */
+ if (next != end) {
+ PyObject *strchunk;
+ APPEND_OLD_CHUNK
+ strchunk = PyString_FromStringAndSize(&buf[end], next - end);
+ if (strchunk == NULL) {
+ goto bail;
+ }
+ if (has_unicode) {
+ chunk = PyUnicode_FromEncodedObject(strchunk, encoding, NULL);
+ Py_DECREF(strchunk);
+ if (chunk == NULL) {
+ goto bail;
+ }
+ }
+ else {
+ chunk = strchunk;
+ }
+ }
+ next++;
+ if (c == '"') {
+ end = next;
+ break;
+ }
+ if (next == len) {
+ raise_errmsg("Unterminated string starting at", pystr, begin);
+ goto bail;
+ }
+ c = buf[next];
+ if (c != 'u') {
+ /* Non-unicode backslash escapes */
+ end = next + 1;
+ switch (c) {
+ case '"': break;
+ case '\\': break;
+ case '/': break;
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ default: c = 0;
+ }
+ if (c == 0) {
+ raise_errmsg("Invalid \\escape", pystr, end - 2);
+ goto bail;
+ }
+ }
+ else {
+ c = 0;
+ next++;
+ end = next + 4;
+ if (end >= len) {
+ raise_errmsg("Invalid \\uXXXX escape", pystr, next - 1);
+ goto bail;
+ }
+ /* Decode 4 hex digits */
+ for (; next < end; next++) {
+ Py_UNICODE digit = buf[next];
+ c <<= 4;
+ switch (digit) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ c |= (digit - '0'); break;
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f':
+ c |= (digit - 'a' + 10); break;
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F':
+ c |= (digit - 'A' + 10); break;
+ default:
+ raise_errmsg("Invalid \\uXXXX escape", pystr, end - 5);
+ goto bail;
+ }
+ }
+#ifdef Py_UNICODE_WIDE
+ /* Surrogate pair */
+ if ((c & 0xfc00) == 0xd800) {
+ Py_UNICODE c2 = 0;
+ if (end + 6 >= len) {
+ raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+ goto bail;
+ }
+ if (buf[next++] != '\\' || buf[next++] != 'u') {
+ raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+ goto bail;
+ }
+ end += 6;
+ /* Decode 4 hex digits */
+ for (; next < end; next++) {
+ c2 <<= 4;
+ Py_UNICODE digit = buf[next];
+ switch (digit) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ c2 |= (digit - '0'); break;
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f':
+ c2 |= (digit - 'a' + 10); break;
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F':
+ c2 |= (digit - 'A' + 10); break;
+ default:
+ raise_errmsg("Invalid \\uXXXX escape", pystr, end - 5);
+ goto bail;
+ }
+ }
+ if ((c2 & 0xfc00) != 0xdc00) {
+ raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+ goto bail;
+ }
+ c = 0x10000 + (((c - 0xd800) << 10) | (c2 - 0xdc00));
+ }
+ else if ((c & 0xfc00) == 0xdc00) {
+ raise_errmsg("Unpaired low surrogate", pystr, end - 5);
+ goto bail;
+ }
+#endif
+ }
+ if (c > 0x7f) {
+ has_unicode = 1;
+ }
+ APPEND_OLD_CHUNK
+ if (has_unicode) {
+ chunk = PyUnicode_FromUnicode(&c, 1);
+ if (chunk == NULL) {
+ goto bail;
+ }
+ }
+ else {
+ char c_char = Py_CHARMASK(c);
+ chunk = PyString_FromStringAndSize(&c_char, 1);
+ if (chunk == NULL) {
+ goto bail;
+ }
+ }
+ }
+
+ if (chunks == NULL) {
+ if (chunk != NULL)
+ rval = chunk;
+ else
+ rval = PyString_FromStringAndSize("", 0);
+ }
+ else {
+ APPEND_OLD_CHUNK
+ rval = join_list_string(chunks);
+ if (rval == NULL) {
+ goto bail;
+ }
+ Py_CLEAR(chunks);
+ }
+
+ *next_end_ptr = end;
+ return rval;
+bail:
+ *next_end_ptr = -1;
+ Py_XDECREF(chunk);
+ Py_XDECREF(chunks);
+ return NULL;
+}
+
+
+static PyObject *
+scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next_end_ptr)
+{
+ /* Read the JSON string from PyUnicode pystr.
+ end is the index of the first character after the quote.
+ if strict is zero then literal control characters are allowed
+ *next_end_ptr is a return-by-reference index of the character
+ after the end quote
+
+ Return value is a new PyUnicode
+ */
+ PyObject *rval;
+ Py_ssize_t len = PyUnicode_GET_SIZE(pystr);
+ Py_ssize_t begin = end - 1;
+ Py_ssize_t next = begin;
+ const Py_UNICODE *buf = PyUnicode_AS_UNICODE(pystr);
+ PyObject *chunks = NULL;
+ PyObject *chunk = NULL;
+
+ if (len == end) {
+ raise_errmsg("Unterminated string starting at", pystr, begin);
+ }
+ else if (end < 0 || len < end) {
+ PyErr_SetString(PyExc_ValueError, "end is out of bounds");
+ goto bail;
+ }
+ while (1) {
+ /* Find the end of the string or the next escape */
+ Py_UNICODE c = 0;
+ for (next = end; next < len; next++) {
+ c = buf[next];
+ if (c == '"' || c == '\\') {
+ break;
+ }
+ else if (strict && c <= 0x1f) {
+ raise_errmsg("Invalid control character at", pystr, next);
+ goto bail;
+ }
+ }
+ if (!(c == '"' || c == '\\')) {
+ raise_errmsg("Unterminated string starting at", pystr, begin);
+ goto bail;
+ }
+ /* Pick up this chunk if it's not zero length */
+ if (next != end) {
+ APPEND_OLD_CHUNK
+ chunk = PyUnicode_FromUnicode(&buf[end], next - end);
+ if (chunk == NULL) {
+ goto bail;
+ }
+ }
+ next++;
+ if (c == '"') {
+ end = next;
+ break;
+ }
+ if (next == len) {
+ raise_errmsg("Unterminated string starting at", pystr, begin);
+ goto bail;
+ }
+ c = buf[next];
+ if (c != 'u') {
+ /* Non-unicode backslash escapes */
+ end = next + 1;
+ switch (c) {
+ case '"': break;
+ case '\\': break;
+ case '/': break;
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ default: c = 0;
+ }
+ if (c == 0) {
+ raise_errmsg("Invalid \\escape", pystr, end - 2);
+ goto bail;
+ }
+ }
+ else {
+ c = 0;
+ next++;
+ end = next + 4;
+ if (end >= len) {
+ raise_errmsg("Invalid \\uXXXX escape", pystr, next - 1);
+ goto bail;
+ }
+ /* Decode 4 hex digits */
+ for (; next < end; next++) {
+ Py_UNICODE digit = buf[next];
+ c <<= 4;
+ switch (digit) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ c |= (digit - '0'); break;
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f':
+ c |= (digit - 'a' + 10); break;
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F':
+ c |= (digit - 'A' + 10); break;
+ default:
+ raise_errmsg("Invalid \\uXXXX escape", pystr, end - 5);
+ goto bail;
+ }
+ }
+#ifdef Py_UNICODE_WIDE
+ /* Surrogate pair */
+ if ((c & 0xfc00) == 0xd800) {
+ Py_UNICODE c2 = 0;
+ if (end + 6 >= len) {
+ raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+ goto bail;
+ }
+ if (buf[next++] != '\\' || buf[next++] != 'u') {
+ raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+ goto bail;
+ }
+ end += 6;
+ /* Decode 4 hex digits */
+ for (; next < end; next++) {
+ c2 <<= 4;
+ Py_UNICODE digit = buf[next];
+ switch (digit) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ c2 |= (digit - '0'); break;
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f':
+ c2 |= (digit - 'a' + 10); break;
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F':
+ c2 |= (digit - 'A' + 10); break;
+ default:
+ raise_errmsg("Invalid \\uXXXX escape", pystr, end - 5);
+ goto bail;
+ }
+ }
+ if ((c2 & 0xfc00) != 0xdc00) {
+ raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+ goto bail;
+ }
+ c = 0x10000 + (((c - 0xd800) << 10) | (c2 - 0xdc00));
+ }
+ else if ((c & 0xfc00) == 0xdc00) {
+ raise_errmsg("Unpaired low surrogate", pystr, end - 5);
+ goto bail;
+ }
+#endif
+ }
+ APPEND_OLD_CHUNK
+ chunk = PyUnicode_FromUnicode(&c, 1);
+ if (chunk == NULL) {
+ goto bail;
+ }
+ }
+
+ if (chunks == NULL) {
+ if (chunk != NULL)
+ rval = chunk;
+ else
+ rval = PyUnicode_FromUnicode(NULL, 0);
+ }
+ else {
+ APPEND_OLD_CHUNK
+ rval = join_list_unicode(chunks);
+ if (rval == NULL) {
+ goto bail;
+ }
+ Py_CLEAR(chunks);
+ }
+ *next_end_ptr = end;
+ return rval;
+bail:
+ *next_end_ptr = -1;
+ Py_XDECREF(chunk);
+ Py_XDECREF(chunks);
+ return NULL;
+}
+
+PyDoc_STRVAR(pydoc_scanstring,
+ "scanstring(basestring, end, encoding, strict=True) -> (str, end)\n"
+ "\n"
+ "Scan the string s for a JSON string. End is the index of the\n"
+ "character in s after the quote that started the JSON string.\n"
+ "Unescapes all valid JSON string escape sequences and raises ValueError\n"
+ "on attempt to decode an invalid string. If strict is False then literal\n"
+ "control characters are allowed in the string.\n"
+ "\n"
+ "Returns a tuple of the decoded string and the index of the character in s\n"
+ "after the end quote."
+);
+
+static PyObject *
+py_scanstring(PyObject* self UNUSED, PyObject *args)
+{
+ PyObject *pystr;
+ PyObject *rval;
+ Py_ssize_t end;
+ Py_ssize_t next_end = -1;
+ char *encoding = NULL;
+ int strict = 1;
+ if (!PyArg_ParseTuple(args, "OO&|zi:scanstring", &pystr, _convertPyInt_AsSsize_t, &end, &encoding, &strict)) {
+ return NULL;
+ }
+ if (encoding == NULL) {
+ encoding = DEFAULT_ENCODING;
+ }
+ if (PyString_Check(pystr)) {
+ rval = scanstring_str(pystr, end, encoding, strict, &next_end);
+ }
+ else if (PyUnicode_Check(pystr)) {
+ rval = scanstring_unicode(pystr, end, strict, &next_end);
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "first argument must be a string, not %.80s",
+ Py_TYPE(pystr)->tp_name);
+ return NULL;
+ }
+ return _build_rval_index_tuple(rval, next_end);
+}
+
+PyDoc_STRVAR(pydoc_encode_basestring_ascii,
+ "encode_basestring_ascii(basestring) -> str\n"
+ "\n"
+ "Return an ASCII-only JSON representation of a Python string"
+);
+
+static PyObject *
+py_encode_basestring_ascii(PyObject* self UNUSED, PyObject *pystr)
+{
+ /* Return an ASCII-only JSON representation of a Python string */
+ /* METH_O */
+ if (PyString_Check(pystr)) {
+ return ascii_escape_str(pystr);
+ }
+ else if (PyUnicode_Check(pystr)) {
+ return ascii_escape_unicode(pystr);
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "first argument must be a string, not %.80s",
+ Py_TYPE(pystr)->tp_name);
+ return NULL;
+ }
+}
+
+static void
+scanner_dealloc(PyObject *self)
+{
+ /* Deallocate scanner object */
+ scanner_clear(self);
+ Py_TYPE(self)->tp_free(self);
+}
+
+static int
+scanner_traverse(PyObject *self, visitproc visit, void *arg)
+{
+ PyScannerObject *s;
+ assert(PyScanner_Check(self));
+ s = (PyScannerObject *)self;
+ Py_VISIT(s->encoding);
+ Py_VISIT(s->strict);
+ Py_VISIT(s->object_hook);
+ Py_VISIT(s->pairs_hook);
+ Py_VISIT(s->parse_float);
+ Py_VISIT(s->parse_int);
+ Py_VISIT(s->parse_constant);
+ Py_VISIT(s->memo);
+ return 0;
+}
+
+static int
+scanner_clear(PyObject *self)
+{
+ PyScannerObject *s;
+ assert(PyScanner_Check(self));
+ s = (PyScannerObject *)self;
+ Py_CLEAR(s->encoding);
+ Py_CLEAR(s->strict);
+ Py_CLEAR(s->object_hook);
+ Py_CLEAR(s->pairs_hook);
+ Py_CLEAR(s->parse_float);
+ Py_CLEAR(s->parse_int);
+ Py_CLEAR(s->parse_constant);
+ Py_CLEAR(s->memo);
+ return 0;
+}
+
+static PyObject *
+_parse_object_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) {
+ /* Read a JSON object from PyString pystr.
+ idx is the index of the first character after the opening curly brace.
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the closing curly brace.
+
+ Returns a new PyObject (usually a dict, but object_hook or
+ object_pairs_hook can change that)
+ */
+ char *str = PyString_AS_STRING(pystr);
+ Py_ssize_t end_idx = PyString_GET_SIZE(pystr) - 1;
+ PyObject *rval = NULL;
+ PyObject *pairs = NULL;
+ PyObject *item;
+ PyObject *key = NULL;
+ PyObject *val = NULL;
+ char *encoding = PyString_AS_STRING(s->encoding);
+ int strict = PyObject_IsTrue(s->strict);
+ int has_pairs_hook = (s->pairs_hook != Py_None);
+ Py_ssize_t next_idx;
+ if (has_pairs_hook) {
+ pairs = PyList_New(0);
+ if (pairs == NULL)
+ return NULL;
+ }
+ else {
+ rval = PyDict_New();
+ if (rval == NULL)
+ return NULL;
+ }
+
+ /* skip whitespace after { */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* only loop if the object is non-empty */
+ if (idx <= end_idx && str[idx] != '}') {
+ while (idx <= end_idx) {
+ PyObject *memokey;
+
+ /* read key */
+ if (str[idx] != '"') {
+ raise_errmsg(
+ "Expecting property name enclosed in double quotes",
+ pystr, idx);
+ goto bail;
+ }
+ key = scanstring_str(pystr, idx + 1, encoding, strict, &next_idx);
+ if (key == NULL)
+ goto bail;
+ memokey = PyDict_GetItem(s->memo, key);
+ if (memokey != NULL) {
+ Py_INCREF(memokey);
+ Py_DECREF(key);
+ key = memokey;
+ }
+ else {
+ if (PyDict_SetItem(s->memo, key, key) < 0)
+ goto bail;
+ }
+ idx = next_idx;
+
+ /* skip whitespace between key and : delimiter, read :, skip whitespace */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+ if (idx > end_idx || str[idx] != ':') {
+ raise_errmsg("Expecting ':' delimiter", pystr, idx);
+ goto bail;
+ }
+ idx++;
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* read any JSON data type */
+ val = scan_once_str(s, pystr, idx, &next_idx);
+ if (val == NULL)
+ goto bail;
+
+ if (has_pairs_hook) {
+ item = PyTuple_Pack(2, key, val);
+ if (item == NULL)
+ goto bail;
+ Py_CLEAR(key);
+ Py_CLEAR(val);
+ if (PyList_Append(pairs, item) == -1) {
+ Py_DECREF(item);
+ goto bail;
+ }
+ Py_DECREF(item);
+ }
+ else {
+ if (PyDict_SetItem(rval, key, val) < 0)
+ goto bail;
+ Py_CLEAR(key);
+ Py_CLEAR(val);
+ }
+ idx = next_idx;
+
+ /* skip whitespace before } or , */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* bail if the object is closed or we didn't get the , delimiter */
+ if (idx > end_idx) break;
+ if (str[idx] == '}') {
+ break;
+ }
+ else if (str[idx] != ',') {
+ raise_errmsg("Expecting ',' delimiter", pystr, idx);
+ goto bail;
+ }
+ idx++;
+
+ /* skip whitespace after , delimiter */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+ }
+ }
+ /* verify that idx < end_idx, str[idx] should be '}' */
+ if (idx > end_idx || str[idx] != '}') {
+ raise_errmsg("Expecting object", pystr, end_idx);
+ goto bail;
+ }
+
+ /* if pairs_hook is not None: rval = object_pairs_hook(pairs) */
+ if (s->pairs_hook != Py_None) {
+ val = PyObject_CallFunctionObjArgs(s->pairs_hook, pairs, NULL);
+ if (val == NULL)
+ goto bail;
+ Py_DECREF(pairs);
+ *next_idx_ptr = idx + 1;
+ return val;
+ }
+
+ /* if object_hook is not None: rval = object_hook(rval) */
+ if (s->object_hook != Py_None) {
+ val = PyObject_CallFunctionObjArgs(s->object_hook, rval, NULL);
+ if (val == NULL)
+ goto bail;
+ Py_DECREF(rval);
+ rval = val;
+ val = NULL;
+ }
+ *next_idx_ptr = idx + 1;
+ return rval;
+bail:
+ Py_XDECREF(rval);
+ Py_XDECREF(key);
+ Py_XDECREF(val);
+ Py_XDECREF(pairs);
+ return NULL;
+}
+
+static PyObject *
+_parse_object_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) {
+ /* Read a JSON object from PyUnicode pystr.
+ idx is the index of the first character after the opening curly brace.
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the closing curly brace.
+
+ Returns a new PyObject (usually a dict, but object_hook can change that)
+ */
+ Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr);
+ Py_ssize_t end_idx = PyUnicode_GET_SIZE(pystr) - 1;
+ PyObject *rval = NULL;
+ PyObject *pairs = NULL;
+ PyObject *item;
+ PyObject *key = NULL;
+ PyObject *val = NULL;
+ int strict = PyObject_IsTrue(s->strict);
+ int has_pairs_hook = (s->pairs_hook != Py_None);
+ Py_ssize_t next_idx;
+
+ if (has_pairs_hook) {
+ pairs = PyList_New(0);
+ if (pairs == NULL)
+ return NULL;
+ }
+ else {
+ rval = PyDict_New();
+ if (rval == NULL)
+ return NULL;
+ }
+
+ /* skip whitespace after { */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* only loop if the object is non-empty */
+ if (idx <= end_idx && str[idx] != '}') {
+ while (idx <= end_idx) {
+ PyObject *memokey;
+
+ /* read key */
+ if (str[idx] != '"') {
+ raise_errmsg(
+ "Expecting property name enclosed in double quotes",
+ pystr, idx);
+ goto bail;
+ }
+ key = scanstring_unicode(pystr, idx + 1, strict, &next_idx);
+ if (key == NULL)
+ goto bail;
+ memokey = PyDict_GetItem(s->memo, key);
+ if (memokey != NULL) {
+ Py_INCREF(memokey);
+ Py_DECREF(key);
+ key = memokey;
+ }
+ else {
+ if (PyDict_SetItem(s->memo, key, key) < 0)
+ goto bail;
+ }
+ idx = next_idx;
+
+ /* skip whitespace between key and : delimiter, read :, skip
+ whitespace */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+ if (idx > end_idx || str[idx] != ':') {
+ raise_errmsg("Expecting ':' delimiter", pystr, idx);
+ goto bail;
+ }
+ idx++;
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* read any JSON term */
+ val = scan_once_unicode(s, pystr, idx, &next_idx);
+ if (val == NULL)
+ goto bail;
+
+ if (has_pairs_hook) {
+ item = PyTuple_Pack(2, key, val);
+ if (item == NULL)
+ goto bail;
+ Py_CLEAR(key);
+ Py_CLEAR(val);
+ if (PyList_Append(pairs, item) == -1) {
+ Py_DECREF(item);
+ goto bail;
+ }
+ Py_DECREF(item);
+ }
+ else {
+ if (PyDict_SetItem(rval, key, val) < 0)
+ goto bail;
+ Py_CLEAR(key);
+ Py_CLEAR(val);
+ }
+ idx = next_idx;
+
+ /* skip whitespace before } or , */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* bail if the object is closed or we didn't get the ,
+ delimiter */
+ if (idx > end_idx) break;
+ if (str[idx] == '}') {
+ break;
+ }
+ else if (str[idx] != ',') {
+ raise_errmsg("Expecting ',' delimiter", pystr, idx);
+ goto bail;
+ }
+ idx++;
+
+ /* skip whitespace after , delimiter */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+ }
+ }
+
+ /* verify that idx < end_idx, str[idx] should be '}' */
+ if (idx > end_idx || str[idx] != '}') {
+ raise_errmsg("Expecting object", pystr, end_idx);
+ goto bail;
+ }
+
+ /* if pairs_hook is not None: rval = object_pairs_hook(pairs) */
+ if (s->pairs_hook != Py_None) {
+ val = PyObject_CallFunctionObjArgs(s->pairs_hook, pairs, NULL);
+ if (val == NULL)
+ goto bail;
+ Py_DECREF(pairs);
+ *next_idx_ptr = idx + 1;
+ return val;
+ }
+
+ /* if object_hook is not None: rval = object_hook(rval) */
+ if (s->object_hook != Py_None) {
+ val = PyObject_CallFunctionObjArgs(s->object_hook, rval, NULL);
+ if (val == NULL)
+ goto bail;
+ Py_DECREF(rval);
+ rval = val;
+ val = NULL;
+ }
+ *next_idx_ptr = idx + 1;
+ return rval;
+bail:
+ Py_XDECREF(rval);
+ Py_XDECREF(key);
+ Py_XDECREF(val);
+ Py_XDECREF(pairs);
+ return NULL;
+}
+
+static PyObject *
+_parse_array_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) {
+ /* Read a JSON array from PyString pystr.
+ idx is the index of the first character after the opening brace.
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the closing brace.
+
+ Returns a new PyList
+ */
+ char *str = PyString_AS_STRING(pystr);
+ Py_ssize_t end_idx = PyString_GET_SIZE(pystr) - 1;
+ PyObject *val = NULL;
+ PyObject *rval = PyList_New(0);
+ Py_ssize_t next_idx;
+ if (rval == NULL)
+ return NULL;
+
+ /* skip whitespace after [ */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* only loop if the array is non-empty */
+ if (idx <= end_idx && str[idx] != ']') {
+ while (idx <= end_idx) {
+
+ /* read any JSON term and de-tuplefy the (rval, idx) */
+ val = scan_once_str(s, pystr, idx, &next_idx);
+ if (val == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
+ PyErr_Clear();
+ raise_errmsg("Expecting object", pystr, idx);
+ }
+ goto bail;
+ }
+
+ if (PyList_Append(rval, val) == -1)
+ goto bail;
+
+ Py_CLEAR(val);
+ idx = next_idx;
+
+ /* skip whitespace between term and , */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* bail if the array is closed or we didn't get the , delimiter */
+ if (idx > end_idx) break;
+ if (str[idx] == ']') {
+ break;
+ }
+ else if (str[idx] != ',') {
+ raise_errmsg("Expecting ',' delimiter", pystr, idx);
+ goto bail;
+ }
+ idx++;
+
+ /* skip whitespace after , */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+ }
+ }
+
+ /* verify that idx < end_idx, str[idx] should be ']' */
+ if (idx > end_idx || str[idx] != ']') {
+ raise_errmsg("Expecting object", pystr, end_idx);
+ goto bail;
+ }
+ *next_idx_ptr = idx + 1;
+ return rval;
+bail:
+ Py_XDECREF(val);
+ Py_DECREF(rval);
+ return NULL;
+}
+
+static PyObject *
+_parse_array_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) {
+ /* Read a JSON array from PyString pystr.
+ idx is the index of the first character after the opening brace.
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the closing brace.
+
+ Returns a new PyList
+ */
+ Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr);
+ Py_ssize_t end_idx = PyUnicode_GET_SIZE(pystr) - 1;
+ PyObject *val = NULL;
+ PyObject *rval = PyList_New(0);
+ Py_ssize_t next_idx;
+ if (rval == NULL)
+ return NULL;
+
+ /* skip whitespace after [ */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* only loop if the array is non-empty */
+ if (idx <= end_idx && str[idx] != ']') {
+ while (idx <= end_idx) {
+
+ /* read any JSON term */
+ val = scan_once_unicode(s, pystr, idx, &next_idx);
+ if (val == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
+ PyErr_Clear();
+ raise_errmsg("Expecting object", pystr, idx);
+ }
+ goto bail;
+ }
+
+ if (PyList_Append(rval, val) == -1)
+ goto bail;
+
+ Py_CLEAR(val);
+ idx = next_idx;
+
+ /* skip whitespace between term and , */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* bail if the array is closed or we didn't get the , delimiter */
+ if (idx > end_idx) break;
+ if (str[idx] == ']') {
+ break;
+ }
+ else if (str[idx] != ',') {
+ raise_errmsg("Expecting ',' delimiter", pystr, idx);
+ goto bail;
+ }
+ idx++;
+
+ /* skip whitespace after , */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+ }
+ }
+
+ /* verify that idx < end_idx, str[idx] should be ']' */
+ if (idx > end_idx || str[idx] != ']') {
+ raise_errmsg("Expecting object", pystr, end_idx);
+ goto bail;
+ }
+ *next_idx_ptr = idx + 1;
+ return rval;
+bail:
+ Py_XDECREF(val);
+ Py_DECREF(rval);
+ return NULL;
+}
+
+static PyObject *
+_parse_constant(PyScannerObject *s, char *constant, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) {
+ /* Read a JSON constant from PyString pystr.
+ constant is the constant string that was found
+ ("NaN", "Infinity", "-Infinity").
+ idx is the index of the first character of the constant
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the constant.
+
+ Returns the result of parse_constant
+ */
+ PyObject *cstr;
+ PyObject *rval;
+ /* constant is "NaN", "Infinity", or "-Infinity" */
+ cstr = PyString_InternFromString(constant);
+ if (cstr == NULL)
+ return NULL;
+
+ /* rval = parse_constant(constant) */
+ rval = PyObject_CallFunctionObjArgs(s->parse_constant, cstr, NULL);
+ idx += PyString_GET_SIZE(cstr);
+ Py_DECREF(cstr);
+ *next_idx_ptr = idx;
+ return rval;
+}
+
+static PyObject *
+_match_number_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t start, Py_ssize_t *next_idx_ptr) {
+ /* Read a JSON number from PyString pystr.
+ idx is the index of the first character of the number
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the number.
+
+ Returns a new PyObject representation of that number:
+ PyInt, PyLong, or PyFloat.
+ May return other types if parse_int or parse_float are set
+ */
+ char *str = PyString_AS_STRING(pystr);
+ Py_ssize_t end_idx = PyString_GET_SIZE(pystr) - 1;
+ Py_ssize_t idx = start;
+ int is_float = 0;
+ PyObject *rval;
+ PyObject *numstr;
+
+ /* read a sign if it's there, make sure it's not the end of the string */
+ if (str[idx] == '-') {
+ idx++;
+ if (idx > end_idx) {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+ }
+
+ /* read as many integer digits as we find as long as it doesn't start with 0 */
+ if (str[idx] >= '1' && str[idx] <= '9') {
+ idx++;
+ while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+ }
+ /* if it starts with 0 we only expect one integer digit */
+ else if (str[idx] == '0') {
+ idx++;
+ }
+ /* no integer digits, error */
+ else {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+
+ /* if the next char is '.' followed by a digit then read all float digits */
+ if (idx < end_idx && str[idx] == '.' && str[idx + 1] >= '0' && str[idx + 1] <= '9') {
+ is_float = 1;
+ idx += 2;
+ while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+ }
+
+ /* if the next char is 'e' or 'E' then maybe read the exponent (or backtrack) */
+ if (idx < end_idx && (str[idx] == 'e' || str[idx] == 'E')) {
+
+ /* save the index of the 'e' or 'E' just in case we need to backtrack */
+ Py_ssize_t e_start = idx;
+ idx++;
+
+ /* read an exponent sign if present */
+ if (idx < end_idx && (str[idx] == '-' || str[idx] == '+')) idx++;
+
+ /* read all digits */
+ while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+
+ /* if we got a digit, then parse as float. if not, backtrack */
+ if (str[idx - 1] >= '0' && str[idx - 1] <= '9') {
+ is_float = 1;
+ }
+ else {
+ idx = e_start;
+ }
+ }
+
+ /* copy the section we determined to be a number */
+ numstr = PyString_FromStringAndSize(&str[start], idx - start);
+ if (numstr == NULL)
+ return NULL;
+ if (is_float) {
+ /* parse as a float using a fast path if available, otherwise call user defined method */
+ if (s->parse_float != (PyObject *)&PyFloat_Type) {
+ rval = PyObject_CallFunctionObjArgs(s->parse_float, numstr, NULL);
+ }
+ else {
+ /* rval = PyFloat_FromDouble(PyOS_ascii_atof(PyString_AS_STRING(numstr))); */
+ double d = PyOS_string_to_double(PyString_AS_STRING(numstr),
+ NULL, NULL);
+ if (d == -1.0 && PyErr_Occurred())
+ return NULL;
+ rval = PyFloat_FromDouble(d);
+ }
+ }
+ else {
+ /* parse as an int using a fast path if available, otherwise call user defined method */
+ if (s->parse_int != (PyObject *)&PyInt_Type) {
+ rval = PyObject_CallFunctionObjArgs(s->parse_int, numstr, NULL);
+ }
+ else {
+ rval = PyInt_FromString(PyString_AS_STRING(numstr), NULL, 10);
+ }
+ }
+ Py_DECREF(numstr);
+ *next_idx_ptr = idx;
+ return rval;
+}
+
+static PyObject *
+_match_number_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t start, Py_ssize_t *next_idx_ptr) {
+ /* Read a JSON number from PyUnicode pystr.
+ idx is the index of the first character of the number
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the number.
+
+ Returns a new PyObject representation of that number:
+ PyInt, PyLong, or PyFloat.
+ May return other types if parse_int or parse_float are set
+ */
+ Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr);
+ Py_ssize_t end_idx = PyUnicode_GET_SIZE(pystr) - 1;
+ Py_ssize_t idx = start;
+ int is_float = 0;
+ PyObject *rval;
+ PyObject *numstr;
+
+ /* read a sign if it's there, make sure it's not the end of the string */
+ if (str[idx] == '-') {
+ idx++;
+ if (idx > end_idx) {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+ }
+
+ /* read as many integer digits as we find as long as it doesn't start with 0 */
+ if (str[idx] >= '1' && str[idx] <= '9') {
+ idx++;
+ while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+ }
+ /* if it starts with 0 we only expect one integer digit */
+ else if (str[idx] == '0') {
+ idx++;
+ }
+ /* no integer digits, error */
+ else {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+
+ /* if the next char is '.' followed by a digit then read all float digits */
+ if (idx < end_idx && str[idx] == '.' && str[idx + 1] >= '0' && str[idx + 1] <= '9') {
+ is_float = 1;
+ idx += 2;
+ while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+ }
+
+ /* if the next char is 'e' or 'E' then maybe read the exponent (or backtrack) */
+ if (idx < end_idx && (str[idx] == 'e' || str[idx] == 'E')) {
+ Py_ssize_t e_start = idx;
+ idx++;
+
+ /* read an exponent sign if present */
+ if (idx < end_idx && (str[idx] == '-' || str[idx] == '+')) idx++;
+
+ /* read all digits */
+ while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+
+ /* if we got a digit, then parse as float. if not, backtrack */
+ if (str[idx - 1] >= '0' && str[idx - 1] <= '9') {
+ is_float = 1;
+ }
+ else {
+ idx = e_start;
+ }
+ }
+
+ /* copy the section we determined to be a number */
+ numstr = PyUnicode_FromUnicode(&str[start], idx - start);
+ if (numstr == NULL)
+ return NULL;
+ if (is_float) {
+ /* parse as a float using a fast path if available, otherwise call user defined method */
+ if (s->parse_float != (PyObject *)&PyFloat_Type) {
+ rval = PyObject_CallFunctionObjArgs(s->parse_float, numstr, NULL);
+ }
+ else {
+ rval = PyFloat_FromString(numstr, NULL);
+ }
+ }
+ else {
+ /* no fast path for unicode -> int, just call */
+ rval = PyObject_CallFunctionObjArgs(s->parse_int, numstr, NULL);
+ }
+ Py_DECREF(numstr);
+ *next_idx_ptr = idx;
+ return rval;
+}
+
+static PyObject *
+scan_once_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr)
+{
+ /* Read one JSON term (of any kind) from PyString pystr.
+ idx is the index of the first character of the term
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the number.
+
+ Returns a new PyObject representation of the term.
+ */
+ char *str = PyString_AS_STRING(pystr);
+ Py_ssize_t length = PyString_GET_SIZE(pystr);
+ PyObject *rval = NULL;
+ int fallthrough = 0;
+ if (idx >= length) {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+ if (Py_EnterRecursiveCall(" while decoding a JSON document"))
+ return NULL;
+ switch (str[idx]) {
+ case '"':
+ /* string */
+ rval = scanstring_str(pystr, idx + 1,
+ PyString_AS_STRING(s->encoding),
+ PyObject_IsTrue(s->strict),
+ next_idx_ptr);
+ break;
+ case '{':
+ /* object */
+ rval = _parse_object_str(s, pystr, idx + 1, next_idx_ptr);
+ break;
+ case '[':
+ /* array */
+ rval = _parse_array_str(s, pystr, idx + 1, next_idx_ptr);
+ break;
+ case 'n':
+ /* null */
+ if ((idx + 3 < length) && str[idx + 1] == 'u' && str[idx + 2] == 'l' && str[idx + 3] == 'l') {
+ Py_INCREF(Py_None);
+ *next_idx_ptr = idx + 4;
+ rval = Py_None;
+ }
+ else
+ fallthrough = 1;
+ break;
+ case 't':
+ /* true */
+ if ((idx + 3 < length) && str[idx + 1] == 'r' && str[idx + 2] == 'u' && str[idx + 3] == 'e') {
+ Py_INCREF(Py_True);
+ *next_idx_ptr = idx + 4;
+ rval = Py_True;
+ }
+ else
+ fallthrough = 1;
+ break;
+ case 'f':
+ /* false */
+ if ((idx + 4 < length) && str[idx + 1] == 'a' && str[idx + 2] == 'l' && str[idx + 3] == 's' && str[idx + 4] == 'e') {
+ Py_INCREF(Py_False);
+ *next_idx_ptr = idx + 5;
+ rval = Py_False;
+ }
+ else
+ fallthrough = 1;
+ break;
+ case 'N':
+ /* NaN */
+ if ((idx + 2 < length) && str[idx + 1] == 'a' && str[idx + 2] == 'N') {
+ rval = _parse_constant(s, "NaN", idx, next_idx_ptr);
+ }
+ else
+ fallthrough = 1;
+ break;
+ case 'I':
+ /* Infinity */
+ if ((idx + 7 < length) && str[idx + 1] == 'n' && str[idx + 2] == 'f' && str[idx + 3] == 'i' && str[idx + 4] == 'n' && str[idx + 5] == 'i' && str[idx + 6] == 't' && str[idx + 7] == 'y') {
+ rval = _parse_constant(s, "Infinity", idx, next_idx_ptr);
+ }
+ else
+ fallthrough = 1;
+ break;
+ case '-':
+ /* -Infinity */
+ if ((idx + 8 < length) && str[idx + 1] == 'I' && str[idx + 2] == 'n' && str[idx + 3] == 'f' && str[idx + 4] == 'i' && str[idx + 5] == 'n' && str[idx + 6] == 'i' && str[idx + 7] == 't' && str[idx + 8] == 'y') {
+ rval = _parse_constant(s, "-Infinity", idx, next_idx_ptr);
+ }
+ else
+ fallthrough = 1;
+ break;
+ default:
+ fallthrough = 1;
+ }
+ /* Didn't find a string, object, array, or named constant. Look for a number. */
+ if (fallthrough)
+ rval = _match_number_str(s, pystr, idx, next_idx_ptr);
+ Py_LeaveRecursiveCall();
+ return rval;
+}
+
+static PyObject *
+scan_once_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr)
+{
+ /* Read one JSON term (of any kind) from PyUnicode pystr.
+ idx is the index of the first character of the term
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the number.
+
+ Returns a new PyObject representation of the term.
+ */
+ Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr);
+ Py_ssize_t length = PyUnicode_GET_SIZE(pystr);
+ PyObject *rval = NULL;
+ int fallthrough = 0;
+ if (idx >= length) {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+ if (Py_EnterRecursiveCall(" while decoding a JSON document"))
+ return NULL;
+ switch (str[idx]) {
+ case '"':
+ /* string */
+ rval = scanstring_unicode(pystr, idx + 1,
+ PyObject_IsTrue(s->strict),
+ next_idx_ptr);
+ break;
+ case '{':
+ /* object */
+ rval = _parse_object_unicode(s, pystr, idx + 1, next_idx_ptr);
+ break;
+ case '[':
+ /* array */
+ rval = _parse_array_unicode(s, pystr, idx + 1, next_idx_ptr);
+ break;
+ case 'n':
+ /* null */
+ if ((idx + 3 < length) && str[idx + 1] == 'u' && str[idx + 2] == 'l' && str[idx + 3] == 'l') {
+ Py_INCREF(Py_None);
+ *next_idx_ptr = idx + 4;
+ rval = Py_None;
+ }
+ else
+ fallthrough = 1;
+ break;
+ case 't':
+ /* true */
+ if ((idx + 3 < length) && str[idx + 1] == 'r' && str[idx + 2] == 'u' && str[idx + 3] == 'e') {
+ Py_INCREF(Py_True);
+ *next_idx_ptr = idx + 4;
+ rval = Py_True;
+ }
+ else
+ fallthrough = 1;
+ break;
+ case 'f':
+ /* false */
+ if ((idx + 4 < length) && str[idx + 1] == 'a' && str[idx + 2] == 'l' && str[idx + 3] == 's' && str[idx + 4] == 'e') {
+ Py_INCREF(Py_False);
+ *next_idx_ptr = idx + 5;
+ rval = Py_False;
+ }
+ else
+ fallthrough = 1;
+ break;
+ case 'N':
+ /* NaN */
+ if ((idx + 2 < length) && str[idx + 1] == 'a' && str[idx + 2] == 'N') {
+ rval = _parse_constant(s, "NaN", idx, next_idx_ptr);
+ }
+ else
+ fallthrough = 1;
+ break;
+ case 'I':
+ /* Infinity */
+ if ((idx + 7 < length) && str[idx + 1] == 'n' && str[idx + 2] == 'f' && str[idx + 3] == 'i' && str[idx + 4] == 'n' && str[idx + 5] == 'i' && str[idx + 6] == 't' && str[idx + 7] == 'y') {
+ rval = _parse_constant(s, "Infinity", idx, next_idx_ptr);
+ }
+ else
+ fallthrough = 1;
+ break;
+ case '-':
+ /* -Infinity */
+ if ((idx + 8 < length) && str[idx + 1] == 'I' && str[idx + 2] == 'n' && str[idx + 3] == 'f' && str[idx + 4] == 'i' && str[idx + 5] == 'n' && str[idx + 6] == 'i' && str[idx + 7] == 't' && str[idx + 8] == 'y') {
+ rval = _parse_constant(s, "-Infinity", idx, next_idx_ptr);
+ }
+ else
+ fallthrough = 1;
+ break;
+ default:
+ fallthrough = 1;
+ }
+ /* Didn't find a string, object, array, or named constant. Look for a number. */
+ if (fallthrough)
+ rval = _match_number_unicode(s, pystr, idx, next_idx_ptr);
+ Py_LeaveRecursiveCall();
+ return rval;
+}
+
+static PyObject *
+scanner_call(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ /* Python callable interface to scan_once_{str,unicode} */
+ PyObject *pystr;
+ PyObject *rval;
+ Py_ssize_t idx;
+ Py_ssize_t next_idx = -1;
+ static char *kwlist[] = {"string", "idx", NULL};
+ PyScannerObject *s;
+ assert(PyScanner_Check(self));
+ s = (PyScannerObject *)self;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO&:scan_once", kwlist, &pystr, _convertPyInt_AsSsize_t, &idx))
+ return NULL;
+
+ if (PyString_Check(pystr)) {
+ rval = scan_once_str(s, pystr, idx, &next_idx);
+ }
+ else if (PyUnicode_Check(pystr)) {
+ rval = scan_once_unicode(s, pystr, idx, &next_idx);
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "first argument must be a string, not %.80s",
+ Py_TYPE(pystr)->tp_name);
+ return NULL;
+ }
+ PyDict_Clear(s->memo);
+ return _build_rval_index_tuple(rval, next_idx);
+}
+
+static PyObject *
+scanner_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyScannerObject *s;
+ s = (PyScannerObject *)type->tp_alloc(type, 0);
+ if (s != NULL) {
+ s->encoding = NULL;
+ s->strict = NULL;
+ s->object_hook = NULL;
+ s->pairs_hook = NULL;
+ s->parse_float = NULL;
+ s->parse_int = NULL;
+ s->parse_constant = NULL;
+ }
+ return (PyObject *)s;
+}
+
+static int
+scanner_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ /* Initialize Scanner object */
+ PyObject *ctx;
+ static char *kwlist[] = {"context", NULL};
+ PyScannerObject *s;
+
+ assert(PyScanner_Check(self));
+ s = (PyScannerObject *)self;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:make_scanner", kwlist, &ctx))
+ return -1;
+
+ if (s->memo == NULL) {
+ s->memo = PyDict_New();
+ if (s->memo == NULL)
+ goto bail;
+ }
+
+ /* PyString_AS_STRING is used on encoding */
+ s->encoding = PyObject_GetAttrString(ctx, "encoding");
+ if (s->encoding == NULL)
+ goto bail;
+ if (s->encoding == Py_None) {
+ Py_DECREF(Py_None);
+ s->encoding = PyString_InternFromString(DEFAULT_ENCODING);
+ }
+ else if (PyUnicode_Check(s->encoding)) {
+ PyObject *tmp = PyUnicode_AsEncodedString(s->encoding, NULL, NULL);
+ Py_DECREF(s->encoding);
+ s->encoding = tmp;
+ }
+ if (s->encoding == NULL || !PyString_Check(s->encoding))
+ goto bail;
+
+ /* All of these will fail "gracefully" so we don't need to verify them */
+ s->strict = PyObject_GetAttrString(ctx, "strict");
+ if (s->strict == NULL)
+ goto bail;
+ s->object_hook = PyObject_GetAttrString(ctx, "object_hook");
+ if (s->object_hook == NULL)
+ goto bail;
+ s->pairs_hook = PyObject_GetAttrString(ctx, "object_pairs_hook");
+ if (s->pairs_hook == NULL)
+ goto bail;
+ s->parse_float = PyObject_GetAttrString(ctx, "parse_float");
+ if (s->parse_float == NULL)
+ goto bail;
+ s->parse_int = PyObject_GetAttrString(ctx, "parse_int");
+ if (s->parse_int == NULL)
+ goto bail;
+ s->parse_constant = PyObject_GetAttrString(ctx, "parse_constant");
+ if (s->parse_constant == NULL)
+ goto bail;
+
+ return 0;
+
+bail:
+ Py_CLEAR(s->encoding);
+ Py_CLEAR(s->strict);
+ Py_CLEAR(s->object_hook);
+ Py_CLEAR(s->pairs_hook);
+ Py_CLEAR(s->parse_float);
+ Py_CLEAR(s->parse_int);
+ Py_CLEAR(s->parse_constant);
+ return -1;
+}
+
+PyDoc_STRVAR(scanner_doc, "JSON scanner object");
+
+static
+PyTypeObject PyScannerType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* tp_internal */
+ "simplejson._speedups.Scanner", /* tp_name */
+ sizeof(PyScannerObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ scanner_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ scanner_call, /* tp_call */
+ 0, /* tp_str */
+ 0,/* PyObject_GenericGetAttr, */ /* tp_getattro */
+ 0,/* PyObject_GenericSetAttr, */ /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
+ scanner_doc, /* tp_doc */
+ scanner_traverse, /* tp_traverse */
+ scanner_clear, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ scanner_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ scanner_init, /* tp_init */
+ 0,/* PyType_GenericAlloc, */ /* tp_alloc */
+ scanner_new, /* tp_new */
+ 0,/* PyObject_GC_Del, */ /* tp_free */
+};
+
+static PyObject *
+encoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyEncoderObject *s;
+ s = (PyEncoderObject *)type->tp_alloc(type, 0);
+ if (s != NULL) {
+ s->markers = NULL;
+ s->defaultfn = NULL;
+ s->encoder = NULL;
+ s->indent = NULL;
+ s->key_separator = NULL;
+ s->item_separator = NULL;
+ s->sort_keys = NULL;
+ s->skipkeys = NULL;
+ s->key_memo = NULL;
+ s->item_sort_key = NULL;
+ s->Decimal = NULL;
+ }
+ return (PyObject *)s;
+}
+
+static int
+encoder_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ /* initialize Encoder object */
+ static char *kwlist[] = {"markers", "default", "encoder", "indent", "key_separator", "item_separator", "sort_keys", "skipkeys", "allow_nan", "key_memo", "use_decimal", "namedtuple_as_object", "tuple_as_array", "bigint_as_string", "item_sort_key", "Decimal", NULL};
+
+ PyEncoderObject *s;
+ PyObject *markers, *defaultfn, *encoder, *indent, *key_separator;
+ PyObject *item_separator, *sort_keys, *skipkeys, *allow_nan, *key_memo;
+ PyObject *use_decimal, *namedtuple_as_object, *tuple_as_array;
+ PyObject *bigint_as_string, *item_sort_key, *Decimal;
+
+ assert(PyEncoder_Check(self));
+ s = (PyEncoderObject *)self;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOOOOOOOOOOOOOOO:make_encoder", kwlist,
+ &markers, &defaultfn, &encoder, &indent, &key_separator, &item_separator,
+ &sort_keys, &skipkeys, &allow_nan, &key_memo, &use_decimal,
+ &namedtuple_as_object, &tuple_as_array, &bigint_as_string,
+ &item_sort_key, &Decimal))
+ return -1;
+
+ s->markers = markers;
+ s->defaultfn = defaultfn;
+ s->encoder = encoder;
+ s->indent = indent;
+ s->key_separator = key_separator;
+ s->item_separator = item_separator;
+ s->sort_keys = sort_keys;
+ s->skipkeys = skipkeys;
+ s->key_memo = key_memo;
+ s->fast_encode = (PyCFunction_Check(s->encoder) && PyCFunction_GetFunction(s->encoder) == (PyCFunction)py_encode_basestring_ascii);
+ s->allow_nan = PyObject_IsTrue(allow_nan);
+ s->use_decimal = PyObject_IsTrue(use_decimal);
+ s->namedtuple_as_object = PyObject_IsTrue(namedtuple_as_object);
+ s->tuple_as_array = PyObject_IsTrue(tuple_as_array);
+ s->bigint_as_string = PyObject_IsTrue(bigint_as_string);
+ s->item_sort_key = item_sort_key;
+ s->Decimal = Decimal;
+
+ Py_INCREF(s->markers);
+ Py_INCREF(s->defaultfn);
+ Py_INCREF(s->encoder);
+ Py_INCREF(s->indent);
+ Py_INCREF(s->key_separator);
+ Py_INCREF(s->item_separator);
+ Py_INCREF(s->sort_keys);
+ Py_INCREF(s->skipkeys);
+ Py_INCREF(s->key_memo);
+ Py_INCREF(s->item_sort_key);
+ Py_INCREF(s->Decimal);
+ return 0;
+}
+
+static PyObject *
+encoder_call(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ /* Python callable interface to encode_listencode_obj */
+ static char *kwlist[] = {"obj", "_current_indent_level", NULL};
+ PyObject *obj;
+ PyObject *rval;
+ Py_ssize_t indent_level;
+ PyEncoderObject *s;
+ assert(PyEncoder_Check(self));
+ s = (PyEncoderObject *)self;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO&:_iterencode", kwlist,
+ &obj, _convertPyInt_AsSsize_t, &indent_level))
+ return NULL;
+ rval = PyList_New(0);
+ if (rval == NULL)
+ return NULL;
+ if (encoder_listencode_obj(s, rval, obj, indent_level)) {
+ Py_DECREF(rval);
+ return NULL;
+ }
+ return rval;
+}
+
+static PyObject *
+_encoded_const(PyObject *obj)
+{
+ /* Return the JSON string representation of None, True, False */
+ if (obj == Py_None) {
+ static PyObject *s_null = NULL;
+ if (s_null == NULL) {
+ s_null = PyString_InternFromString("null");
+ }
+ Py_INCREF(s_null);
+ return s_null;
+ }
+ else if (obj == Py_True) {
+ static PyObject *s_true = NULL;
+ if (s_true == NULL) {
+ s_true = PyString_InternFromString("true");
+ }
+ Py_INCREF(s_true);
+ return s_true;
+ }
+ else if (obj == Py_False) {
+ static PyObject *s_false = NULL;
+ if (s_false == NULL) {
+ s_false = PyString_InternFromString("false");
+ }
+ Py_INCREF(s_false);
+ return s_false;
+ }
+ else {
+ PyErr_SetString(PyExc_ValueError, "not a const");
+ return NULL;
+ }
+}
+
+static PyObject *
+encoder_encode_float(PyEncoderObject *s, PyObject *obj)
+{
+ /* Return the JSON representation of a PyFloat */
+ double i = PyFloat_AS_DOUBLE(obj);
+ if (!Py_IS_FINITE(i)) {
+ if (!s->allow_nan) {
+ PyErr_SetString(PyExc_ValueError, "Out of range float values are not JSON compliant");
+ return NULL;
+ }
+ if (i > 0) {
+ return PyString_FromString("Infinity");
+ }
+ else if (i < 0) {
+ return PyString_FromString("-Infinity");
+ }
+ else {
+ return PyString_FromString("NaN");
+ }
+ }
+ /* Use a better float format here? */
+ return PyObject_Repr(obj);
+}
+
+static PyObject *
+encoder_encode_string(PyEncoderObject *s, PyObject *obj)
+{
+ /* Return the JSON representation of a string */
+ if (s->fast_encode)
+ return py_encode_basestring_ascii(NULL, obj);
+ else
+ return PyObject_CallFunctionObjArgs(s->encoder, obj, NULL);
+}
+
+static int
+_steal_list_append(PyObject *lst, PyObject *stolen)
+{
+ /* Append stolen and then decrement its reference count */
+ int rval = PyList_Append(lst, stolen);
+ Py_DECREF(stolen);
+ return rval;
+}
+
+static int
+encoder_listencode_obj(PyEncoderObject *s, PyObject *rval, PyObject *obj, Py_ssize_t indent_level)
+{
+ /* Encode Python object obj to a JSON term, rval is a PyList */
+ int rv = -1;
+ if (Py_EnterRecursiveCall(" while encoding a JSON document"))
+ return rv;
+ do {
+ if (obj == Py_None || obj == Py_True || obj == Py_False) {
+ PyObject *cstr = _encoded_const(obj);
+ if (cstr != NULL)
+ rv = _steal_list_append(rval, cstr);
+ }
+ else if (PyString_Check(obj) || PyUnicode_Check(obj))
+ {
+ PyObject *encoded = encoder_encode_string(s, obj);
+ if (encoded != NULL)
+ rv = _steal_list_append(rval, encoded);
+ }
+ else if (PyInt_Check(obj) || PyLong_Check(obj)) {
+ PyObject *encoded = PyObject_Str(obj);
+ if (encoded != NULL) {
+ if (s->bigint_as_string) {
+ encoded = maybe_quote_bigint(encoded, obj);
+ if (encoded == NULL)
+ break;
+ }
+ rv = _steal_list_append(rval, encoded);
+ }
+ }
+ else if (PyFloat_Check(obj)) {
+ PyObject *encoded = encoder_encode_float(s, obj);
+ if (encoded != NULL)
+ rv = _steal_list_append(rval, encoded);
+ }
+ else if (s->namedtuple_as_object && _is_namedtuple(obj)) {
+ PyObject *newobj = PyObject_CallMethod(obj, "_asdict", NULL);
+ if (newobj != NULL) {
+ rv = encoder_listencode_dict(s, rval, newobj, indent_level);
+ Py_DECREF(newobj);
+ }
+ }
+ else if (PyList_Check(obj) || (s->tuple_as_array && PyTuple_Check(obj))) {
+ rv = encoder_listencode_list(s, rval, obj, indent_level);
+ }
+ else if (PyDict_Check(obj)) {
+ rv = encoder_listencode_dict(s, rval, obj, indent_level);
+ }
+ else if (s->use_decimal && PyObject_TypeCheck(obj, s->Decimal)) {
+ PyObject *encoded = PyObject_Str(obj);
+ if (encoded != NULL)
+ rv = _steal_list_append(rval, encoded);
+ }
+ else {
+ PyObject *ident = NULL;
+ PyObject *newobj;
+ if (s->markers != Py_None) {
+ int has_key;
+ ident = PyLong_FromVoidPtr(obj);
+ if (ident == NULL)
+ break;
+ has_key = PyDict_Contains(s->markers, ident);
+ if (has_key) {
+ if (has_key != -1)
+ PyErr_SetString(PyExc_ValueError, "Circular reference detected");
+ Py_DECREF(ident);
+ break;
+ }
+ if (PyDict_SetItem(s->markers, ident, obj)) {
+ Py_DECREF(ident);
+ break;
+ }
+ }
+ newobj = PyObject_CallFunctionObjArgs(s->defaultfn, obj, NULL);
+ if (newobj == NULL) {
+ Py_XDECREF(ident);
+ break;
+ }
+ rv = encoder_listencode_obj(s, rval, newobj, indent_level);
+ Py_DECREF(newobj);
+ if (rv) {
+ Py_XDECREF(ident);
+ rv = -1;
+ }
+ else if (ident != NULL) {
+ if (PyDict_DelItem(s->markers, ident)) {
+ Py_XDECREF(ident);
+ rv = -1;
+ }
+ Py_XDECREF(ident);
+ }
+ }
+ } while (0);
+ Py_LeaveRecursiveCall();
+ return rv;
+}
+
+static int
+encoder_listencode_dict(PyEncoderObject *s, PyObject *rval, PyObject *dct, Py_ssize_t indent_level)
+{
+ /* Encode Python dict dct a JSON term, rval is a PyList */
+ static PyObject *open_dict = NULL;
+ static PyObject *close_dict = NULL;
+ static PyObject *empty_dict = NULL;
+ static PyObject *iteritems = NULL;
+ PyObject *kstr = NULL;
+ PyObject *ident = NULL;
+ PyObject *iter = NULL;
+ PyObject *item = NULL;
+ PyObject *items = NULL;
+ PyObject *encoded = NULL;
+ int skipkeys;
+ Py_ssize_t idx;
+
+ if (open_dict == NULL || close_dict == NULL || empty_dict == NULL || iteritems == NULL) {
+ open_dict = PyString_InternFromString("{");
+ close_dict = PyString_InternFromString("}");
+ empty_dict = PyString_InternFromString("{}");
+ iteritems = PyString_InternFromString("iteritems");
+ if (open_dict == NULL || close_dict == NULL || empty_dict == NULL || iteritems == NULL)
+ return -1;
+ }
+ if (PyDict_Size(dct) == 0)
+ return PyList_Append(rval, empty_dict);
+
+ if (s->markers != Py_None) {
+ int has_key;
+ ident = PyLong_FromVoidPtr(dct);
+ if (ident == NULL)
+ goto bail;
+ has_key = PyDict_Contains(s->markers, ident);
+ if (has_key) {
+ if (has_key != -1)
+ PyErr_SetString(PyExc_ValueError, "Circular reference detected");
+ goto bail;
+ }
+ if (PyDict_SetItem(s->markers, ident, dct)) {
+ goto bail;
+ }
+ }
+
+ if (PyList_Append(rval, open_dict))
+ goto bail;
+
+ if (s->indent != Py_None) {
+ /* TODO: DOES NOT RUN */
+ indent_level += 1;
+ /*
+ newline_indent = '\n' + (_indent * _current_indent_level)
+ separator = _item_separator + newline_indent
+ buf += newline_indent
+ */
+ }
+
+ if (PyCallable_Check(s->item_sort_key)) {
+ if (PyDict_CheckExact(dct))
+ items = PyDict_Items(dct);
+ else
+ items = PyMapping_Items(dct);
+ PyObject_CallMethod(items, "sort", "OO", Py_None, s->item_sort_key);
+ }
+ else if (PyObject_IsTrue(s->sort_keys)) {
+ /* First sort the keys then replace them with (key, value) tuples. */
+ Py_ssize_t i, nitems;
+ if (PyDict_CheckExact(dct))
+ items = PyDict_Keys(dct);
+ else
+ items = PyMapping_Keys(dct);
+ if (items == NULL)
+ goto bail;
+ if (!PyList_Check(items)) {
+ PyErr_SetString(PyExc_ValueError, "keys must return list");
+ goto bail;
+ }
+ if (PyList_Sort(items) < 0)
+ goto bail;
+ nitems = PyList_GET_SIZE(items);
+ for (i = 0; i < nitems; i++) {
+ PyObject *key, *value;
+ key = PyList_GET_ITEM(items, i);
+ value = PyDict_GetItem(dct, key);
+ item = PyTuple_Pack(2, key, value);
+ if (item == NULL)
+ goto bail;
+ PyList_SET_ITEM(items, i, item);
+ Py_DECREF(key);
+ }
+ }
+ else {
+ if (PyDict_CheckExact(dct))
+ items = PyDict_Items(dct);
+ else
+ items = PyMapping_Items(dct);
+ }
+ if (items == NULL)
+ goto bail;
+ iter = PyObject_GetIter(items);
+ Py_DECREF(items);
+ if (iter == NULL)
+ goto bail;
+
+ skipkeys = PyObject_IsTrue(s->skipkeys);
+ idx = 0;
+ while ((item = PyIter_Next(iter))) {
+ PyObject *encoded, *key, *value;
+ if (!PyTuple_Check(item) || Py_SIZE(item) != 2) {
+ PyErr_SetString(PyExc_ValueError, "items must return 2-tuples");
+ goto bail;
+ }
+ key = PyTuple_GET_ITEM(item, 0);
+ if (key == NULL)
+ goto bail;
+ value = PyTuple_GET_ITEM(item, 1);
+ if (value == NULL)
+ goto bail;
+
+ encoded = PyDict_GetItem(s->key_memo, key);
+ if (encoded != NULL) {
+ Py_INCREF(encoded);
+ }
+ else if (PyString_Check(key) || PyUnicode_Check(key)) {
+ Py_INCREF(key);
+ kstr = key;
+ }
+ else if (PyFloat_Check(key)) {
+ kstr = encoder_encode_float(s, key);
+ if (kstr == NULL)
+ goto bail;
+ }
+ else if (key == Py_True || key == Py_False || key == Py_None) {
+ /* This must come before the PyInt_Check because
+ True and False are also 1 and 0.*/
+ kstr = _encoded_const(key);
+ if (kstr == NULL)
+ goto bail;
+ }
+ else if (PyInt_Check(key) || PyLong_Check(key)) {
+ kstr = PyObject_Str(key);
+ if (kstr == NULL)
+ goto bail;
+ }
+ else if (skipkeys) {
+ Py_DECREF(item);
+ continue;
+ }
+ else {
+ /* TODO: include repr of key */
+ PyErr_SetString(PyExc_TypeError, "keys must be a string");
+ goto bail;
+ }
+
+ if (idx) {
+ if (PyList_Append(rval, s->item_separator))
+ goto bail;
+ }
+
+ if (encoded == NULL) {
+ encoded = encoder_encode_string(s, kstr);
+ Py_CLEAR(kstr);
+ if (encoded == NULL)
+ goto bail;
+ if (PyDict_SetItem(s->key_memo, key, encoded))
+ goto bail;
+ }
+ if (PyList_Append(rval, encoded)) {
+ goto bail;
+ }
+ Py_CLEAR(encoded);
+ if (PyList_Append(rval, s->key_separator))
+ goto bail;
+ if (encoder_listencode_obj(s, rval, value, indent_level))
+ goto bail;
+ Py_CLEAR(item);
+ idx += 1;
+ }
+ Py_CLEAR(iter);
+ if (PyErr_Occurred())
+ goto bail;
+ if (ident != NULL) {
+ if (PyDict_DelItem(s->markers, ident))
+ goto bail;
+ Py_CLEAR(ident);
+ }
+ if (s->indent != Py_None) {
+ /* TODO: DOES NOT RUN */
+ indent_level -= 1;
+ /*
+ yield '\n' + (_indent * _current_indent_level)
+ */
+ }
+ if (PyList_Append(rval, close_dict))
+ goto bail;
+ return 0;
+
+bail:
+ Py_XDECREF(encoded);
+ Py_XDECREF(items);
+ Py_XDECREF(iter);
+ Py_XDECREF(kstr);
+ Py_XDECREF(ident);
+ return -1;
+}
+
+
+static int
+encoder_listencode_list(PyEncoderObject *s, PyObject *rval, PyObject *seq, Py_ssize_t indent_level)
+{
+ /* Encode Python list seq to a JSON term, rval is a PyList */
+ static PyObject *open_array = NULL;
+ static PyObject *close_array = NULL;
+ static PyObject *empty_array = NULL;
+ PyObject *ident = NULL;
+ PyObject *iter = NULL;
+ PyObject *obj = NULL;
+ int is_true;
+ int i = 0;
+
+ if (open_array == NULL || close_array == NULL || empty_array == NULL) {
+ open_array = PyString_InternFromString("[");
+ close_array = PyString_InternFromString("]");
+ empty_array = PyString_InternFromString("[]");
+ if (open_array == NULL || close_array == NULL || empty_array == NULL)
+ return -1;
+ }
+ ident = NULL;
+ is_true = PyObject_IsTrue(seq);
+ if (is_true == -1)
+ return -1;
+ else if (is_true == 0)
+ return PyList_Append(rval, empty_array);
+
+ if (s->markers != Py_None) {
+ int has_key;
+ ident = PyLong_FromVoidPtr(seq);
+ if (ident == NULL)
+ goto bail;
+ has_key = PyDict_Contains(s->markers, ident);
+ if (has_key) {
+ if (has_key != -1)
+ PyErr_SetString(PyExc_ValueError, "Circular reference detected");
+ goto bail;
+ }
+ if (PyDict_SetItem(s->markers, ident, seq)) {
+ goto bail;
+ }
+ }
+
+ iter = PyObject_GetIter(seq);
+ if (iter == NULL)
+ goto bail;
+
+ if (PyList_Append(rval, open_array))
+ goto bail;
+ if (s->indent != Py_None) {
+ /* TODO: DOES NOT RUN */
+ indent_level += 1;
+ /*
+ newline_indent = '\n' + (_indent * _current_indent_level)
+ separator = _item_separator + newline_indent
+ buf += newline_indent
+ */
+ }
+ while ((obj = PyIter_Next(iter))) {
+ if (i) {
+ if (PyList_Append(rval, s->item_separator))
+ goto bail;
+ }
+ if (encoder_listencode_obj(s, rval, obj, indent_level))
+ goto bail;
+ i++;
+ Py_CLEAR(obj);
+ }
+ Py_CLEAR(iter);
+ if (PyErr_Occurred())
+ goto bail;
+ if (ident != NULL) {
+ if (PyDict_DelItem(s->markers, ident))
+ goto bail;
+ Py_CLEAR(ident);
+ }
+ if (s->indent != Py_None) {
+ /* TODO: DOES NOT RUN */
+ indent_level -= 1;
+ /*
+ yield '\n' + (_indent * _current_indent_level)
+ */
+ }
+ if (PyList_Append(rval, close_array))
+ goto bail;
+ return 0;
+
+bail:
+ Py_XDECREF(obj);
+ Py_XDECREF(iter);
+ Py_XDECREF(ident);
+ return -1;
+}
+
+static void
+encoder_dealloc(PyObject *self)
+{
+ /* Deallocate Encoder */
+ encoder_clear(self);
+ Py_TYPE(self)->tp_free(self);
+}
+
+static int
+encoder_traverse(PyObject *self, visitproc visit, void *arg)
+{
+ PyEncoderObject *s;
+ assert(PyEncoder_Check(self));
+ s = (PyEncoderObject *)self;
+ Py_VISIT(s->markers);
+ Py_VISIT(s->defaultfn);
+ Py_VISIT(s->encoder);
+ Py_VISIT(s->indent);
+ Py_VISIT(s->key_separator);
+ Py_VISIT(s->item_separator);
+ Py_VISIT(s->sort_keys);
+ Py_VISIT(s->skipkeys);
+ Py_VISIT(s->key_memo);
+ Py_VISIT(s->item_sort_key);
+ return 0;
+}
+
+static int
+encoder_clear(PyObject *self)
+{
+ /* Deallocate Encoder */
+ PyEncoderObject *s;
+ assert(PyEncoder_Check(self));
+ s = (PyEncoderObject *)self;
+ Py_CLEAR(s->markers);
+ Py_CLEAR(s->defaultfn);
+ Py_CLEAR(s->encoder);
+ Py_CLEAR(s->indent);
+ Py_CLEAR(s->key_separator);
+ Py_CLEAR(s->item_separator);
+ Py_CLEAR(s->sort_keys);
+ Py_CLEAR(s->skipkeys);
+ Py_CLEAR(s->key_memo);
+ Py_CLEAR(s->item_sort_key);
+ Py_CLEAR(s->Decimal);
+ return 0;
+}
+
+PyDoc_STRVAR(encoder_doc, "_iterencode(obj, _current_indent_level) -> iterable");
+
+static
+PyTypeObject PyEncoderType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* tp_internal */
+ "simplejson._speedups.Encoder", /* tp_name */
+ sizeof(PyEncoderObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ encoder_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ encoder_call, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
+ encoder_doc, /* tp_doc */
+ encoder_traverse, /* tp_traverse */
+ encoder_clear, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ encoder_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ encoder_init, /* tp_init */
+ 0, /* tp_alloc */
+ encoder_new, /* tp_new */
+ 0, /* tp_free */
+};
+
+static PyMethodDef speedups_methods[] = {
+ {"encode_basestring_ascii",
+ (PyCFunction)py_encode_basestring_ascii,
+ METH_O,
+ pydoc_encode_basestring_ascii},
+ {"scanstring",
+ (PyCFunction)py_scanstring,
+ METH_VARARGS,
+ pydoc_scanstring},
+ {NULL, NULL, 0, NULL}
+};
+
+PyDoc_STRVAR(module_doc,
+"simplejson speedups\n");
+
+void
+init_speedups(void)
+{
+ PyObject *m;
+ PyScannerType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyScannerType) < 0)
+ return;
+ PyEncoderType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyEncoderType) < 0)
+ return;
+
+
+ m = Py_InitModule3("_speedups", speedups_methods, module_doc);
+ Py_INCREF((PyObject*)&PyScannerType);
+ PyModule_AddObject(m, "make_scanner", (PyObject*)&PyScannerType);
+ Py_INCREF((PyObject*)&PyEncoderType);
+ PyModule_AddObject(m, "make_encoder", (PyObject*)&PyEncoderType);
+}
diff --git a/script/__Lib/diffcalc-2.1/simplejson/decoder.py b/script/__Lib/diffcalc-2.1/simplejson/decoder.py
new file mode 100755
index 0000000..1c1526c
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/simplejson/decoder.py
@@ -0,0 +1,427 @@
+"""Implementation of JSONDecoder
+"""
+import re
+import sys
+import struct
+
+from simplejson.scanner import make_scanner
+def _import_c_scanstring():
+ try:
+ from simplejson._speedups import scanstring
+ return scanstring
+ except ImportError:
+ return None
+c_scanstring = _import_c_scanstring()
+
+__all__ = ['JSONDecoder']
+
+FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
+
+def _floatconstants():
+ _BYTES = '7FF80000000000007FF0000000000000'.decode('hex')
+ # The struct module in Python 2.4 would get frexp() out of range here
+ # when an endian is specified in the format string. Fixed in Python 2.5+
+ if sys.byteorder != 'big':
+ _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1]
+ nan, inf = struct.unpack('dd', _BYTES)
+ return nan, inf, -inf
+
+NaN, PosInf, NegInf = _floatconstants()
+
+
+class JSONDecodeError(ValueError):
+ """Subclass of ValueError with the following additional properties:
+
+ msg: The unformatted error message
+ doc: The JSON document being parsed
+ pos: The start index of doc where parsing failed
+ end: The end index of doc where parsing failed (may be None)
+ lineno: The line corresponding to pos
+ colno: The column corresponding to pos
+ endlineno: The line corresponding to end (may be None)
+ endcolno: The column corresponding to end (may be None)
+
+ """
+ def __init__(self, msg, doc, pos, end=None):
+ ValueError.__init__(self, errmsg(msg, doc, pos, end=end))
+ self.msg = msg
+ self.doc = doc
+ self.pos = pos
+ self.end = end
+ self.lineno, self.colno = linecol(doc, pos)
+ if end is not None:
+ self.endlineno, self.endcolno = linecol(doc, end)
+ else:
+ self.endlineno, self.endcolno = None, None
+
+
+def linecol(doc, pos):
+ lineno = doc.count('\n', 0, pos) + 1
+ if lineno == 1:
+ colno = pos
+ else:
+ colno = pos - doc.rindex('\n', 0, pos)
+ return lineno, colno
+
+
+def errmsg(msg, doc, pos, end=None):
+ # Note that this function is called from _speedups
+ lineno, colno = linecol(doc, pos)
+ if end is None:
+ #fmt = '{0}: line {1} column {2} (char {3})'
+ #return fmt.format(msg, lineno, colno, pos)
+ fmt = '%s: line %d column %d (char %d)'
+ return fmt % (msg, lineno, colno, pos)
+ endlineno, endcolno = linecol(doc, end)
+ #fmt = '{0}: line {1} column {2} - line {3} column {4} (char {5} - {6})'
+ #return fmt.format(msg, lineno, colno, endlineno, endcolno, pos, end)
+ fmt = '%s: line %d column %d - line %d column %d (char %d - %d)'
+ return fmt % (msg, lineno, colno, endlineno, endcolno, pos, end)
+
+
+_CONSTANTS = {
+ '-Infinity': NegInf,
+ 'Infinity': PosInf,
+ 'NaN': NaN,
+}
+
+STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS)
+BACKSLASH = {
+ '"': u'"', '\\': u'\\', '/': u'/',
+ 'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t',
+}
+
+DEFAULT_ENCODING = "utf-8"
+
+def py_scanstring(s, end, encoding=None, strict=True,
+ _b=BACKSLASH, _m=STRINGCHUNK.match):
+ """Scan the string s for a JSON string. End is the index of the
+ character in s after the quote that started the JSON string.
+ Unescapes all valid JSON string escape sequences and raises ValueError
+ on attempt to decode an invalid string. If strict is False then literal
+ control characters are allowed in the string.
+
+ Returns a tuple of the decoded string and the index of the character in s
+ after the end quote."""
+ if encoding is None:
+ encoding = DEFAULT_ENCODING
+ chunks = []
+ _append = chunks.append
+ begin = end - 1
+ while 1:
+ chunk = _m(s, end)
+ if chunk is None:
+ raise JSONDecodeError(
+ "Unterminated string starting at", s, begin)
+ end = chunk.end()
+ content, terminator = chunk.groups()
+ # Content is contains zero or more unescaped string characters
+ if content:
+ if not isinstance(content, unicode):
+ content = unicode(content, encoding)
+ _append(content)
+ # Terminator is the end of string, a literal control character,
+ # or a backslash denoting that an escape sequence follows
+ if terminator == '"':
+ break
+ elif terminator != '\\':
+ if strict:
+ msg = "Invalid control character %r at" % (terminator,)
+ #msg = "Invalid control character {0!r} at".format(terminator)
+ raise JSONDecodeError(msg, s, end)
+ else:
+ _append(terminator)
+ continue
+ try:
+ esc = s[end]
+ except IndexError:
+ raise JSONDecodeError(
+ "Unterminated string starting at", s, begin)
+ # If not a unicode escape sequence, must be in the lookup table
+ if esc != 'u':
+ try:
+ char = _b[esc]
+ except KeyError:
+ msg = "Invalid \\escape: " + repr(esc)
+ raise JSONDecodeError(msg, s, end)
+ end += 1
+ else:
+ # Unicode escape sequence
+ esc = s[end + 1:end + 5]
+ next_end = end + 5
+ if len(esc) != 4:
+ msg = "Invalid \\uXXXX escape"
+ raise JSONDecodeError(msg, s, end)
+ uni = int(esc, 16)
+ # Check for surrogate pair on UCS-4 systems
+ if 0xd800 <= uni <= 0xdbff and sys.maxunicode > 65535:
+ msg = "Invalid \\uXXXX\\uXXXX surrogate pair"
+ if not s[end + 5:end + 7] == '\\u':
+ raise JSONDecodeError(msg, s, end)
+ esc2 = s[end + 7:end + 11]
+ if len(esc2) != 4:
+ raise JSONDecodeError(msg, s, end)
+ uni2 = int(esc2, 16)
+ uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
+ next_end += 6
+ char = unichr(uni)
+ end = next_end
+ # Append the unescaped character
+ _append(char)
+ return u''.join(chunks), end
+
+
+# Use speedup if available
+scanstring = c_scanstring or py_scanstring
+
+WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS)
+WHITESPACE_STR = ' \t\n\r'
+
+def JSONObject((s, end), encoding, strict, scan_once, object_hook,
+ object_pairs_hook, memo=None,
+ _w=WHITESPACE.match, _ws=WHITESPACE_STR):
+ # Backwards compatibility
+ if memo is None:
+ memo = {}
+ memo_get = memo.setdefault
+ pairs = []
+ # Use a slice to prevent IndexError from being raised, the following
+ # check will raise a more specific ValueError if the string is empty
+ nextchar = s[end:end + 1]
+ # Normally we expect nextchar == '"'
+ if nextchar != '"':
+ if nextchar in _ws:
+ end = _w(s, end).end()
+ nextchar = s[end:end + 1]
+ # Trivial empty object
+ if nextchar == '}':
+ if object_pairs_hook is not None:
+ result = object_pairs_hook(pairs)
+ return result, end + 1
+ pairs = {}
+ if object_hook is not None:
+ pairs = object_hook(pairs)
+ return pairs, end + 1
+ elif nextchar != '"':
+ raise JSONDecodeError(
+ "Expecting property name enclosed in double quotes",
+ s, end)
+ end += 1
+ while True:
+ key, end = scanstring(s, end, encoding, strict)
+ key = memo_get(key, key)
+
+ # To skip some function call overhead we optimize the fast paths where
+ # the JSON key separator is ": " or just ":".
+ if s[end:end + 1] != ':':
+ end = _w(s, end).end()
+ if s[end:end + 1] != ':':
+ raise JSONDecodeError("Expecting ':' delimiter", s, end)
+
+ end += 1
+
+ try:
+ if s[end] in _ws:
+ end += 1
+ if s[end] in _ws:
+ end = _w(s, end + 1).end()
+ except IndexError:
+ pass
+
+ try:
+ value, end = scan_once(s, end)
+ except StopIteration:
+ raise JSONDecodeError("Expecting object", s, end)
+ pairs.append((key, value))
+
+ try:
+ nextchar = s[end]
+ if nextchar in _ws:
+ end = _w(s, end + 1).end()
+ nextchar = s[end]
+ except IndexError:
+ nextchar = ''
+ end += 1
+
+ if nextchar == '}':
+ break
+ elif nextchar != ',':
+ raise JSONDecodeError("Expecting ',' delimiter", s, end - 1)
+
+ try:
+ nextchar = s[end]
+ if nextchar in _ws:
+ end += 1
+ nextchar = s[end]
+ if nextchar in _ws:
+ end = _w(s, end + 1).end()
+ nextchar = s[end]
+ except IndexError:
+ nextchar = ''
+
+ end += 1
+ if nextchar != '"':
+ raise JSONDecodeError(
+ "Expecting property name enclosed in double quotes",
+ s, end - 1)
+
+ if object_pairs_hook is not None:
+ result = object_pairs_hook(pairs)
+ return result, end
+ pairs = dict(pairs)
+ if object_hook is not None:
+ pairs = object_hook(pairs)
+ return pairs, end
+
+def JSONArray((s, end), scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
+ values = []
+ nextchar = s[end:end + 1]
+ if nextchar in _ws:
+ end = _w(s, end + 1).end()
+ nextchar = s[end:end + 1]
+ # Look-ahead for trivial empty array
+ if nextchar == ']':
+ return values, end + 1
+ _append = values.append
+ while True:
+ try:
+ value, end = scan_once(s, end)
+ except StopIteration:
+ raise JSONDecodeError("Expecting object", s, end)
+ _append(value)
+ nextchar = s[end:end + 1]
+ if nextchar in _ws:
+ end = _w(s, end + 1).end()
+ nextchar = s[end:end + 1]
+ end += 1
+ if nextchar == ']':
+ break
+ elif nextchar != ',':
+ raise JSONDecodeError("Expecting ',' delimiter", s, end)
+
+ try:
+ if s[end] in _ws:
+ end += 1
+ if s[end] in _ws:
+ end = _w(s, end + 1).end()
+ except IndexError:
+ pass
+
+ return values, end
+
+class JSONDecoder(object):
+ """Simple JSON decoder
+
+ Performs the following translations in decoding by default:
+
+ +---------------+-------------------+
+ | JSON | Python |
+ +===============+===================+
+ | object | dict |
+ +---------------+-------------------+
+ | array | list |
+ +---------------+-------------------+
+ | string | unicode |
+ +---------------+-------------------+
+ | number (int) | int, long |
+ +---------------+-------------------+
+ | number (real) | float |
+ +---------------+-------------------+
+ | true | True |
+ +---------------+-------------------+
+ | false | False |
+ +---------------+-------------------+
+ | null | None |
+ +---------------+-------------------+
+
+ It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as
+ their corresponding ``float`` values, which is outside the JSON spec.
+
+ """
+
+ def __init__(self, encoding=None, object_hook=None, parse_float=None,
+ parse_int=None, parse_constant=None, strict=True,
+ object_pairs_hook=None):
+ """
+ *encoding* determines the encoding used to interpret any
+ :class:`str` objects decoded by this instance (``'utf-8'`` by
+ default). It has no effect when decoding :class:`unicode` objects.
+
+ Note that currently only encodings that are a superset of ASCII work,
+ strings of other encodings should be passed in as :class:`unicode`.
+
+ *object_hook*, if specified, will be called with the result of every
+ JSON object decoded and its return value will be used in place of the
+ given :class:`dict`. This can be used to provide custom
+ deserializations (e.g. to support JSON-RPC class hinting).
+
+ *object_pairs_hook* is an optional function that will be called with
+ the result of any object literal decode with an ordered list of pairs.
+ The return value of *object_pairs_hook* will be used instead of the
+ :class:`dict`. This feature can be used to implement custom decoders
+ that rely on the order that the key and value pairs are decoded (for
+ example, :func:`collections.OrderedDict` will remember the order of
+ insertion). If *object_hook* is also defined, the *object_pairs_hook*
+ takes priority.
+
+ *parse_float*, if specified, will be called with the string of every
+ JSON float to be decoded. By default, this is equivalent to
+ ``float(num_str)``. This can be used to use another datatype or parser
+ for JSON floats (e.g. :class:`decimal.Decimal`).
+
+ *parse_int*, if specified, will be called with the string of every
+ JSON int to be decoded. By default, this is equivalent to
+ ``int(num_str)``. This can be used to use another datatype or parser
+ for JSON integers (e.g. :class:`float`).
+
+ *parse_constant*, if specified, will be called with one of the
+ following strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``. This
+ can be used to raise an exception if invalid JSON numbers are
+ encountered.
+
+ *strict* controls the parser's behavior when it encounters an
+ invalid control character in a string. The default setting of
+ ``True`` means that unescaped control characters are parse errors, if
+ ``False`` then control characters will be allowed in strings.
+
+ """
+ self.encoding = encoding
+ self.object_hook = object_hook
+ self.object_pairs_hook = object_pairs_hook
+ self.parse_float = parse_float or float
+ self.parse_int = parse_int or int
+ self.parse_constant = parse_constant or _CONSTANTS.__getitem__
+ self.strict = strict
+ self.parse_object = JSONObject
+ self.parse_array = JSONArray
+ self.parse_string = scanstring
+ self.memo = {}
+ self.scan_once = make_scanner(self)
+
+ def decode(self, s, _w=WHITESPACE.match):
+ """Return the Python representation of ``s`` (a ``str`` or ``unicode``
+ instance containing a JSON document)
+
+ """
+ obj, end = self.raw_decode(s)
+ end = _w(s, end).end()
+ if end != len(s):
+ raise JSONDecodeError("Extra data", s, end, len(s))
+ return obj
+
+ def raw_decode(self, s, idx=0, _w=WHITESPACE.match):
+ """Decode a JSON document from ``s`` (a ``str`` or ``unicode``
+ beginning with a JSON document) and return a 2-tuple of the Python
+ representation and the index in ``s`` where the document ended.
+ Optionally, ``idx`` can be used to specify an offset in ``s`` where
+ the JSON document begins.
+
+ This can be used to decode a JSON document from a string that may
+ have extraneous data at the end.
+
+ """
+ try:
+ obj, end = self.scan_once(s, idx=_w(s, idx).end())
+ except StopIteration:
+ raise JSONDecodeError("No JSON object could be decoded", s, idx)
+ return obj, end
diff --git a/script/__Lib/diffcalc-2.1/simplejson/encoder.py b/script/__Lib/diffcalc-2.1/simplejson/encoder.py
new file mode 100755
index 0000000..6b4a6a4
--- /dev/null
+++ b/script/__Lib/diffcalc-2.1/simplejson/encoder.py
@@ -0,0 +1,567 @@
+"""Implementation of JSONEncoder
+"""
+import re
+from decimal import Decimal
+
+def _import_speedups():
+ try:
+ from simplejson import _speedups
+ return _speedups.encode_basestring_ascii, _speedups.make_encoder
+ except ImportError:
+ return None, None
+c_encode_basestring_ascii, c_make_encoder = _import_speedups()
+
+from simplejson.decoder import PosInf
+
+ESCAPE = re.compile(ur'[\x00-\x1f\\"\b\f\n\r\t\u2028\u2029]')
+ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
+HAS_UTF8 = re.compile(r'[\x80-\xff]')
+ESCAPE_DCT = {
+ '\\': '\\\\',
+ '"': '\\"',
+ '\b': '\\b',
+ '\f': '\\f',
+ '\n': '\\n',
+ '\r': '\\r',
+ '\t': '\\t',
+ u'\u2028': '\\u2028',
+ u'\u2029': '\\u2029',
+}
+for i in range(0x20):
+ #ESCAPE_DCT.setdefault(chr(i), '\\u{0:04x}'.format(i))
+ ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
+
+FLOAT_REPR = repr
+
+def encode_basestring(s):
+ """Return a JSON representation of a Python string
+
+ """
+ if isinstance(s, str) and HAS_UTF8.search(s) is not None:
+ s = s.decode('utf-8')
+ def replace(match):
+ return ESCAPE_DCT[match.group(0)]
+ return u'"' + ESCAPE.sub(replace, s) + u'"'
+
+
+def py_encode_basestring_ascii(s):
+ """Return an ASCII-only JSON representation of a Python string
+
+ """
+ if isinstance(s, str) and HAS_UTF8.search(s) is not None:
+ s = s.decode('utf-8')
+ def replace(match):
+ s = match.group(0)
+ try:
+ return ESCAPE_DCT[s]
+ except KeyError:
+ n = ord(s)
+ if n < 0x10000:
+ #return '\\u{0:04x}'.format(n)
+ return '\\u%04x' % (n,)
+ else:
+ # surrogate pair
+ n -= 0x10000
+ s1 = 0xd800 | ((n >> 10) & 0x3ff)
+ s2 = 0xdc00 | (n & 0x3ff)
+ #return '\\u{0:04x}\\u{1:04x}'.format(s1, s2)
+ return '\\u%04x\\u%04x' % (s1, s2)
+ return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"'
+
+
+encode_basestring_ascii = (
+ c_encode_basestring_ascii or py_encode_basestring_ascii)
+
+class JSONEncoder(object):
+ """Extensible JSON encoder for Python data structures.
+
+ Supports the following objects and types by default:
+
+ +-------------------+---------------+
+ | Python | JSON |
+ +===================+===============+
+ | dict, namedtuple | object |
+ +-------------------+---------------+
+ | list, tuple | array |
+ +-------------------+---------------+
+ | str, unicode | string |
+ +-------------------+---------------+
+ | int, long, float | number |
+ +-------------------+---------------+
+ | True | true |
+ +-------------------+---------------+
+ | False | false |
+ +-------------------+---------------+
+ | None | null |
+ +-------------------+---------------+
+
+ To extend this to recognize other objects, subclass and implement a
+ ``.default()`` method with another method that returns a serializable
+ object for ``o`` if possible, otherwise it should call the superclass
+ implementation (to raise ``TypeError``).
+
+ """
+ item_separator = ', '
+ key_separator = ': '
+ def __init__(self, skipkeys=False, ensure_ascii=True,
+ check_circular=True, allow_nan=True, sort_keys=False,
+ indent=None, separators=None, encoding='utf-8', default=None,
+ use_decimal=True, namedtuple_as_object=True,
+ tuple_as_array=True, bigint_as_string=False,
+ item_sort_key=None):
+ """Constructor for JSONEncoder, with sensible defaults.
+
+ If skipkeys is false, then it is a TypeError to attempt
+ encoding of keys that are not str, int, long, float or None. If
+ skipkeys is True, such items are simply skipped.
+
+ If ensure_ascii is true, the output is guaranteed to be str
+ objects with all incoming unicode characters escaped. If
+ ensure_ascii is false, the output will be unicode object.
+
+ If check_circular is true, then lists, dicts, and custom encoded
+ objects will be checked for circular references during encoding to
+ prevent an infinite recursion (which would cause an OverflowError).
+ Otherwise, no such check takes place.
+
+ If allow_nan is true, then NaN, Infinity, and -Infinity will be
+ encoded as such. This behavior is not JSON specification compliant,
+ but is consistent with most JavaScript based encoders and decoders.
+ Otherwise, it will be a ValueError to encode such floats.
+
+ If sort_keys is true, then the output of dictionaries will be
+ sorted by key; this is useful for regression tests to ensure
+ that JSON serializations can be compared on a day-to-day basis.
+
+ If indent is a string, then JSON array elements and object members
+ will be pretty-printed with a newline followed by that string repeated
+ for each level of nesting. ``None`` (the default) selects the most compact
+ representation without any newlines. For backwards compatibility with
+ versions of simplejson earlier than 2.1.0, an integer is also accepted
+ and is converted to a string with that many spaces.
+
+ If specified, separators should be a (item_separator, key_separator)
+ tuple. The default is (', ', ': '). To get the most compact JSON
+ representation you should specify (',', ':') to eliminate whitespace.
+
+ If specified, default is a function that gets called for objects
+ that can't otherwise be serialized. It should return a JSON encodable
+ version of the object or raise a ``TypeError``.
+
+ If encoding is not None, then all input strings will be
+ transformed into unicode using that encoding prior to JSON-encoding.
+ The default is UTF-8.
+
+ If use_decimal is true (not the default), ``decimal.Decimal`` will
+ be supported directly by the encoder. For the inverse, decode JSON
+ with ``parse_float=decimal.Decimal``.
+
+ If namedtuple_as_object is true (the default), objects with
+ ``_asdict()`` methods will be encoded as JSON objects.
+
+ If tuple_as_array is true (the default), tuple (and subclasses) will
+ be encoded as JSON arrays.
+
+ If bigint_as_string is true (not the default), ints 2**53 and higher
+ or lower than -2**53 will be encoded as strings. This is to avoid the
+ rounding that happens in Javascript otherwise.
+
+ If specified, item_sort_key is a callable used to sort the items in
+ each dictionary. This is useful if you want to sort items other than
+ in alphabetical order by key.
+ """
+
+ self.skipkeys = skipkeys
+ self.ensure_ascii = ensure_ascii
+ self.check_circular = check_circular
+ self.allow_nan = allow_nan
+ self.sort_keys = sort_keys
+ self.use_decimal = use_decimal
+ self.namedtuple_as_object = namedtuple_as_object
+ self.tuple_as_array = tuple_as_array
+ self.bigint_as_string = bigint_as_string
+ self.item_sort_key = item_sort_key
+ if indent is not None and not isinstance(indent, basestring):
+ indent = indent * ' '
+ self.indent = indent
+ if separators is not None:
+ self.item_separator, self.key_separator = separators
+ elif indent is not None:
+ self.item_separator = ','
+ if default is not None:
+ self.default = default
+ self.encoding = encoding
+
+ def default(self, o):
+ """Implement this method in a subclass such that it returns
+ a serializable object for ``o``, or calls the base implementation
+ (to raise a ``TypeError``).
+
+ For example, to support arbitrary iterators, you could
+ implement default like this::
+
+ def default(self, o):
+ try:
+ iterable = iter(o)
+ except TypeError:
+ pass
+ else:
+ return list(iterable)
+ return JSONEncoder.default(self, o)
+
+ """
+ raise TypeError(repr(o) + " is not JSON serializable")
+
+ def encode(self, o):
+ """Return a JSON string representation of a Python data structure.
+
+ >>> from simplejson import JSONEncoder
+ >>> JSONEncoder().encode({"foo": ["bar", "baz"]})
+ '{"foo": ["bar", "baz"]}'
+
+ """
+ # This is for extremely simple cases and benchmarks.
+ if isinstance(o, basestring):
+ if isinstance(o, str):
+ _encoding = self.encoding
+ if (_encoding is not None
+ and not (_encoding == 'utf-8')):
+ o = o.decode(_encoding)
+ if self.ensure_ascii:
+ return encode_basestring_ascii(o)
+ else:
+ return encode_basestring(o)
+ # This doesn't pass the iterator directly to ''.join() because the
+ # exceptions aren't as detailed. The list call should be roughly
+ # equivalent to the PySequence_Fast that ''.join() would do.
+ chunks = self.iterencode(o, _one_shot=True)
+ if not isinstance(chunks, (list, tuple)):
+ chunks = list(chunks)
+ if self.ensure_ascii:
+ return ''.join(chunks)
+ else:
+ return u''.join(chunks)
+
+ def iterencode(self, o, _one_shot=False):
+ """Encode the given object and yield each string
+ representation as available.
+
+ For example::
+
+ for chunk in JSONEncoder().iterencode(bigobject):
+ mysocket.write(chunk)
+
+ """
+ if self.check_circular:
+ markers = {}
+ else:
+ markers = None
+ if self.ensure_ascii:
+ _encoder = encode_basestring_ascii
+ else:
+ _encoder = encode_basestring
+ if self.encoding != 'utf-8':
+ def _encoder(o, _orig_encoder=_encoder, _encoding=self.encoding):
+ if isinstance(o, str):
+ o = o.decode(_encoding)
+ return _orig_encoder(o)
+
+ def floatstr(o, allow_nan=self.allow_nan,
+ _repr=FLOAT_REPR, _inf=PosInf, _neginf=-PosInf):
+ # Check for specials. Note that this type of test is processor
+ # and/or platform-specific, so do tests which don't depend on
+ # the internals.
+
+ if o != o:
+ text = 'NaN'
+ elif o == _inf:
+ text = 'Infinity'
+ elif o == _neginf:
+ text = '-Infinity'
+ else:
+ return _repr(o)
+
+ if not allow_nan:
+ raise ValueError(
+ "Out of range float values are not JSON compliant: " +
+ repr(o))
+
+ return text
+
+
+ key_memo = {}
+ if (_one_shot and c_make_encoder is not None
+ and self.indent is None):
+ _iterencode = c_make_encoder(
+ markers, self.default, _encoder, self.indent,
+ self.key_separator, self.item_separator, self.sort_keys,
+ self.skipkeys, self.allow_nan, key_memo, self.use_decimal,
+ self.namedtuple_as_object, self.tuple_as_array,
+ self.bigint_as_string, self.item_sort_key,
+ Decimal)
+ else:
+ _iterencode = _make_iterencode(
+ markers, self.default, _encoder, self.indent, floatstr,
+ self.key_separator, self.item_separator, self.sort_keys,
+ self.skipkeys, _one_shot, self.use_decimal,
+ self.namedtuple_as_object, self.tuple_as_array,
+ self.bigint_as_string, self.item_sort_key,
+ Decimal=Decimal)
+ try:
+ return _iterencode(o, 0)
+ finally:
+ key_memo.clear()
+
+
+class JSONEncoderForHTML(JSONEncoder):
+ """An encoder that produces JSON safe to embed in HTML.
+
+ To embed JSON content in, say, a script tag on a web page, the
+ characters &, < and > should be escaped. They cannot be escaped
+ with the usual entities (e.g. &) because they are not expanded
+ within