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