import ch.psi.pshell.device.AccessType; import ch.psi.pshell.device.CameraImageDescriptor; import ch.psi.pshell.device.MatrixCalibration; import ch.psi.pshell.device.Device; import ch.psi.pshell.device.Writable; import java.io.IOException; import java.util.ArrayList; import java.util.List; import ch.psi.pshell.epics.*; import ch.psi.utils.Arr; /** * Implementation of Scienta spectrometer analyser. */ public class Scienta extends AreaDetector { final ChannelInteger acquire; final ChannelInteger energyCount, ThetaYCount, ThetaXCount; final ChannelDouble energyWidth, ThetaYWidth, lowThetaX, highThetaX, stepSizeThetaX, ThetaXWidth; final ControlledVariable lowEnergy, centerEnergy, highEnergy, stepSize, lowThetaY, centerThetaY, highThetaY, stepSizeThetaY, centerThetaX; final ControlledVariable excitationEnergy; final GenericArray data; final ChannelInteger totalSteps, currentStep, exposuresComplete; final DiscretePositioner lensMode, acquisitionMode, energyMode, detectorMode, elementSet, passEnergy; final ChannelString estTime, detectorState; final ChannelByteArray status; final ChannelInteger slices, slicesReadback, channels, channelsReadback; final Stats[] stats; final String channelCtrl; final Device[] monitoredChildren; final boolean test; final ChannelDouble exposureDev; public Scienta(final String name, final String channelPrefix, boolean test) { this(name, channelPrefix + ":cam1", channelPrefix + ":image1", test); } public Scienta(String name, String channelCtrl, String channelData, boolean test) { super(name, channelCtrl, channelData); this.test=test; this.channelCtrl = channelCtrl; acquire = new ChannelInteger(name + " aquire", channelCtrl + ":Acquire", false); lowEnergy = new ControlledVariable(name + " low energy", channelCtrl + ":LOW_ENERGY", channelCtrl + ":LOW_ENERGY_RBV", false); centerEnergy = new ControlledVariable(name + " center energy", channelCtrl + ":CENTRE_ENERGY", channelCtrl + ":CENTRE_ENERGY_RBV", false); highEnergy = new ControlledVariable(name + " high energy", channelCtrl + ":HIGH_ENERGY", channelCtrl + ":HIGH_ENERGY_RBV", false); stepSize = new ControlledVariable(name + " step size", channelCtrl + ":STEP_SIZE", channelCtrl + ":STEP_SIZE_RBV", false); energyWidth = new ChannelDouble(name + " energy width", channelCtrl + ":ENERGY_WIDTH_RBV", 3, false); energyWidth.setAccessType(AccessType.Read); energyCount = new ChannelInteger(name + " energy count", channelCtrl + ":COUNT_RBV", false); energyCount.setAccessType(AccessType.Read); exposureDev = new ChannelDouble(name + " exposure time", channelCtrl + ":AcquireTime", 3, false); lowThetaY = new ControlledVariable(name + " low Theta Y", channelCtrl + ":LOW_THETA_Y", channelCtrl + ":LOW_THETA_Y_RBV", false); centerThetaY = new ControlledVariable(name + " center Theta Y", channelCtrl + ":CENTRE_THETA_Y", channelCtrl + ":CENTRE_THETA_Y_RBV", false); highThetaY = new ControlledVariable(name + " high Theta Y", channelCtrl + ":HIGH_THETA_Y", channelCtrl + ":HIGH_THETA_Y_RBV", false); stepSizeThetaY = new ControlledVariable(name + " Theta Y step size", channelCtrl + ":THETA_Y_STEP_SIZE", channelCtrl + ":THETA_Y_STEP_SIZE_RBV", false); ThetaYWidth = new ChannelDouble(name + " Theta Y width", channelCtrl + ":THETA_Y_WIDTH_RBV", 3, false); ThetaYWidth.setAccessType(AccessType.Read); ThetaYCount = new ChannelInteger(name + " Theta Y count", channelCtrl + ":THETA_Y_COUNT_RBV", false); ThetaYCount.setAccessType(AccessType.Read); lowThetaX = new ChannelDouble(name + " low Theta X", channelCtrl + ":LOW_SLICE_RBV", 3, false); lowThetaX.setAccessType(AccessType.Read); centerThetaX = new ControlledVariable(name + " center Theta X", channelCtrl + ":CENTRE_SLICE", channelCtrl + ":CENTRE_SLICE_RBV", false); highThetaX = new ChannelDouble(name + " high Theta X", channelCtrl + ":HIGH_SLICE_RBV", 3, false); highThetaX.setAccessType(AccessType.Read); stepSizeThetaX = new ChannelDouble(name + " Theta X step size", channelCtrl + ":SLICE_STEP_SIZE_RBV", 3, false); stepSizeThetaX.setAccessType(AccessType.Read); ThetaXWidth = new ChannelDouble(name + " ThetaX width", channelCtrl + ":SLICE_WIDTH_RBV", 3, false); ThetaXWidth.setAccessType(AccessType.Read); ThetaXCount = new ChannelInteger(name + " Theta count", channelCtrl + ":SLICE_COUNT_RBV", false); ThetaXCount.setAccessType(AccessType.Read); passEnergy = new DiscretePositioner(name + " pass energy", channelCtrl + ":PASS_ENERGY", channelCtrl + ":PASS_ENERGY_RBV"); lensMode = new DiscretePositioner(name + " lens mode", channelCtrl + ":LENS_MODE", channelCtrl + ":LENS_MODE_RBV"); acquisitionMode = new DiscretePositioner(name + " acquisition mode", channelCtrl + ":ACQ_MODE", channelCtrl + ":ACQ_MODE_RBV"); energyMode = new DiscretePositioner(name + " energy mode", channelCtrl + ":ENERGY_MODE", channelCtrl + ":ENERGY_MODE_RBV"); detectorMode = new DiscretePositioner(name + " detector mode", channelCtrl + ":DETECTOR_MODE", channelCtrl + ":DETECTOR_MODE_RBV"); elementSet = new DiscretePositioner(name + " element set", channelCtrl + ":ELEMENT_SET_RBV", channelCtrl + ":ELEMENT_SET_RBV"); elementSet.setAccessType(AccessType.Read); slices = new ChannelInteger(name + " slices", channelCtrl + ":SLICES", false); slicesReadback = new ChannelInteger(name + " slices rbv", channelCtrl + ":SLICES_RBV", false); slicesReadback.setAccessType(AccessType.Read); channels = new ChannelInteger(name + " channels", channelCtrl + ":CHANNELS", false); channelsReadback = new ChannelInteger(name + " channels rbv", channelCtrl + ":CHANNELS_RBV", false); channelsReadback.setAccessType(AccessType.Read); excitationEnergy = new ControlledVariable(name + " excitation energy", channelCtrl + ":EXCITATION_ENERGY", channelCtrl + ":EXCITATION_ENERGY_RBV", false); data = new GenericArray(name + " data", channelData + ":ArrayData", SIZE_MAX, false); //If nullable on invalidd value, read blocks on Scienta. data.setAutoResolveType(false); totalSteps = new ChannelInteger(name + " total steps", channelCtrl + ":STEPS_RBV", false); totalSteps.setAccessType(AccessType.Read); currentStep = new ChannelInteger(name + " current step", channelCtrl + ":STEPS_COUNTER_RBV", false); currentStep.setAccessType(AccessType.Read); exposuresComplete = new ChannelInteger(name + " exp complete", channelCtrl + ":NumExposuresCounter_RBV", false); exposuresComplete.setAccessType(AccessType.Read); estTime = new ChannelString(name + " estimated time", channelCtrl + ":ETA_STR", false); estTime.setAccessType(AccessType.Read); detectorState= new ChannelString(name + " detector state", channelCtrl + ":DetectorState_RBV", false); detectorState.setAccessType(AccessType.Read); status= new ChannelByteArray(name + " status", channelCtrl + ":StatusMessage_RBV", -1, false){ protected void onReadout(Object value){ this.setCache((value==null)?"":new String((byte[])value)); } //protected Object convertFromRead(byte[] value){ // return (value==null) ? "" : new String((byte[])value); //} }; status.setAccessType(AccessType.Read); monitoredChildren = new Device[]{acquire, lowEnergy,centerEnergy,highEnergy,energyWidth,energyCount, lowThetaY, centerThetaY, highThetaY, stepSizeThetaY, ThetaYWidth,ThetaYCount, lowThetaX, centerThetaX, highThetaX, stepSizeThetaX, ThetaXWidth,ThetaXCount, stepSize,totalSteps,currentStep, exposuresComplete, passEnergy, lensMode, acquisitionMode, energyMode, detectorMode, elementSet, slices, slicesReadback, channels, channelsReadback, excitationEnergy, exposureDev, estTime, detectorState, status }; addChildren(monitoredChildren); addChildren(new Device[]{ data, }); stats = new Stats[5]; stats[0] = new Stats("CountsR1", 1); stats[1] = new Stats("CountsR2", 2); stats[2] = new Stats("CountsR3", 3); stats[3] = new Stats("CountsR4", 4); stats[4] = new Stats("Counts", 5); addChildren(stats); } @Override protected void doStart() throws IOException, InterruptedException { acquire.write(1); } @Override protected void doSetSimulated() { super.doSetSimulated(); setCache(passEnergy, "2"); setCache(lensMode, "Transmission"); setCache(acquisitionMode, "Fixed"); setCache(energyMode, "Binding"); setCache(detectorMode, "ADC"); setCache(elementSet, "High_Pass_XPS"); } @Override protected void doInitialize() throws IOException, InterruptedException { super.doInitialize(); } @Override protected void doUpdate() throws IOException, InterruptedException { super.doUpdate(); for (Device dev : monitoredChildren){ dev.update(); } } @Override protected void doSetMonitored(boolean value) { super.doSetMonitored(value); for (Device dev : monitoredChildren){ dev.setMonitored(value); } } @Override protected CameraImageDescriptor doReadImageDescriptor() throws IOException, InterruptedException { CameraImageDescriptor ret = super.doReadImageDescriptor(); List channelRange = getChannelRange(); List sliceRange = getSliceRange(); Double cb = channelRange.get(0); Double ce = channelRange.get(1); Double sb = sliceRange.get(0); Double se = sliceRange.get(1); if ((cb == null) || (ce == null) || (sb == null) || (se == null) || (ret.width == 0) || (ret.height == 0)) { ret.calibration = null; } else { double scaleX = (ce - cb) / Math.max(ret.width - 1, 1); double offsetX = cb; double scaleY = (se - sb) / Math.max(ret.height - 1, 1); double offsetY = sb; ret.calibration = new MatrixCalibration(scaleX, scaleY, offsetX, offsetY); } return ret; } //Modes public void setAcquisitionMode(String mode) throws IOException, InterruptedException { //acquisitionMode.write(String.valueOf(mode).replaceAll("_", " ")); acquisitionMode.write(mode); } public String getAcquisitionMode() throws IOException, InterruptedException { return acquisitionMode.getValue(); } public void setEnergyMode(String mode) throws IOException, InterruptedException { energyMode.write(mode); } public String getEnergyMode() throws IOException, InterruptedException { return energyMode.getValue(); } public void setLensMode(String mode) throws IOException, InterruptedException { lensMode.write(mode); } public String getLensMode() throws IOException, InterruptedException { return lensMode.getValue(); } public String[] getLensModes() throws IOException, InterruptedException { return lensMode.getPositions(); } public void setDetectorMode(String mode) throws IOException, InterruptedException { detectorMode.write(mode); } public String getDetectorMode() throws IOException, InterruptedException { return detectorMode.getValue(); } public void setElementSet(String mode) throws IOException, InterruptedException { throw new IOException("Read-only value"); } public String getElementSet() throws IOException, InterruptedException { return elementSet.getValue(); } public void setPassEnergy(int energy) throws IOException, InterruptedException { setPassEnergy(String.valueOf(energy)); } public void setPassEnergy(String energy) throws IOException, InterruptedException { passEnergy.write(energy); } public String getPassEnergy() throws IOException, InterruptedException { return passEnergy.getValue(); } public DiscretePositioner getPassEnergyDev(){ return passEnergy; } public DiscretePositioner getElementSetDev(){ return elementSet; } public DiscretePositioner getDetectorModeDev(){ return detectorMode; } public DiscretePositioner getLensModeDev(){ return lensMode; } public DiscretePositioner getAcquisitionModeDev(){ return acquisitionMode; } public DiscretePositioner getEnergyModeDev(){ return energyMode; } public Writable getRangeDev(){ return (Writable) (Object value) -> { Integer[] range = (Integer[]) value; int[] roi = getROI(); if ((range[0]!=null) && (range[1]!=null)){ int from = Math.min(range[0], range[1]); int to = Math.max(range[0], range[1]); roi[0]=from; roi[2]=(to-from); } if ((range[2]!=null) && (range[3]!=null)){ int from = Math.min(range[2], range[3]); int to = Math.max(range[2], range[3]); roi[1]=from; roi[3]=(to-from); } setROI(roi); }; } public void zeroSupplies() throws IOException, InterruptedException { writeCtrl("ZERO_SUPPLIES", 1); } //Progress //Disconnected operations public double getProgress() { Double cur = currentStep.take().doubleValue(); Double total = totalSteps.take().doubleValue(); if ((cur == null) || (total == null) || (total == 0)) { return 0.0; } return cur / total; } //Direct register access public ChannelInteger getAcquire() { return acquire; } public ControlledVariable getLowEnergy() { return lowEnergy; } public ControlledVariable getCenterEnergy() { return centerEnergy; } public ControlledVariable getHighEnergy() { return highEnergy; } public ControlledVariable getEnergyStepSize() { return stepSize; } public ChannelDouble getEnergyWidth() { return energyWidth; } public ChannelInteger getEnergyCount() { return energyCount; } public ChannelDouble getExposureDev() { return exposureDev; } public ControlledVariable getLowThetaY() { return lowThetaY; } public ControlledVariable getCenterThetaY() { return centerThetaY; } public ControlledVariable getHighThetaY() { return highThetaY; } public ControlledVariable getThetaYStepSize() { return stepSizeThetaY; } public ChannelDouble getThetaYWidth() { return ThetaYWidth; } public ChannelInteger getThetaYCount() { return ThetaYCount; } public ChannelDouble getLowThetaX() { return lowThetaX; } public ControlledVariable getCenterThetaX() { return centerThetaX; } public ChannelDouble getHighThetaX() { return highThetaX; } public ChannelDouble getThetaXStepSize() { return stepSizeThetaX; } public ChannelDouble getThetaXWidth() { return ThetaXWidth; } public ChannelInteger getThetaXCount() { return ThetaXCount; } public ChannelString getEstTime() { return estTime; } public ChannelString getDetectorState() { return detectorState; } public ChannelByteArray getStatus() { return status; } public ChannelInteger getSlices() { return slices; } public ChannelInteger getSlicesReadback() { return slicesReadback; } public ChannelInteger getChannels() { return channels; } public ChannelInteger getChannelsReadback() { return channelsReadback; } public ChannelInteger getTotalSteps() { return totalSteps; } public ChannelInteger getCurrentStep() { return currentStep; } public ChannelInteger getExposuresComplete() { return exposuresComplete; } public ControlledVariable getExcitationEnergy() { return excitationEnergy; } public List getChannelRange() throws IOException, InterruptedException { ArrayList ret = new ArrayList<>(); try { if (isSimulated()){ ret.add(100.0); ret.add(200.0); } else { ret.add(lowEnergy.getReadback().getValue()); ret.add(highEnergy.getReadback().getValue()); } } catch (Exception ex) { ret.add(Double.NaN); ret.add(Double.NaN); } return ret; } public List getSliceRange() throws IOException, InterruptedException { ArrayList ret = new ArrayList<>(); try { if (isSimulated()){ ret.add(200.0); ret.add(400.0); } else { String lens=getLensMode(); if (lens.startsWith("D")){ ret.add(lowThetaY.getReadback().getValue()); ret.add(highThetaY.getReadback().getValue()); } else { ret.add(lowThetaX.getValue()); ret.add(highThetaX.getValue()); } } } catch (Exception ex) { ret.add(Double.NaN); ret.add(Double.NaN); } return ret; } public Integer readNumExposures() throws IOException, InterruptedException{ return (Integer) readCtrl("NumExposures_RBV", Integer.class); } public Double readAcqTime() throws IOException, InterruptedException{ return (Double) readCtrl("AcquireTime_RBV", Double.class); } public Stats[] getStats() { return stats; } public class Stats extends ChannelDouble { final int index; final ChannelInteger uid; Stats(String name, int index) { super(name, channelCtrl.split(":")[0] + ":Stats" + index + ":Total_RBV", 3, false); this.index = index; uid = new ChannelInteger(name + " uid", channelCtrl.split(":")[0] + ":Stats" + index + ":UniqueId_RBV", false); //setParent(Scienta.this); addChild(uid); } @Override public boolean isReady() throws IOException, InterruptedException { Integer imageCounter = getImageCounter().getValue(); if (imageCounter == null) { return false; } /* Integer id = uid.take(); if ((id == null) || (!imageCounter.equals(id))) { uid.update(); } return imageCounter.equals(uid.take()); */ return true; } @Override public Double read() throws IOException, InterruptedException { assertInitialized(); waitReady(10000); return super.read(); } public int getUID() throws IOException, InterruptedException { return uid.getValue(); } } }