Commit 31b84e84 authored by Joel Collins's avatar Joel Collins
Browse files

Added initial calibration modal wizard

parent 1a2822f7
Loading
Loading
Loading
Loading
+20 −4
Original line number Diff line number Diff line
@@ -4,6 +4,11 @@
    class="uk-margin-remove uk-padding-remove uk-height-1-1"
    uk-grid
  >
    <!-- Initialisation modals -->
    <calibrationModal
      ref="calibrationModal"
      :available-plugins="plugins"
    ></calibrationModal>
    <!-- Vertical tab bar -->
    <div
      id="switcher-left"
@@ -167,9 +172,12 @@ import settingsContent from "./tabContentComponents/settingsContent.vue";
import galleryContent from "./tabContentComponents/galleryContent.vue";
import extensionContent from "./tabContentComponents/extensionContent.vue";

// Import modal components for device initialisation
import calibrationModal from "./modalComponents/calibrationModal.vue";

// Export main app
export default {
  name: "PanelLeft",
  name: "AppContent",

  components: {
    tabIcon,
@@ -180,7 +188,8 @@ export default {
    viewContent,
    settingsContent,
    galleryContent,
    extensionContent
    extensionContent,
    calibrationModal
  },

  data: function() {
@@ -220,7 +229,11 @@ export default {
      },
      ready => {
        // Update plugins
        this.updatePlugins();
        this.updatePlugins().then(() => {
          // Start initialisation modals
          this.startModals();
        });

        if (ready) {
          console.log("Left panel now ready");
        } else {
@@ -244,7 +257,7 @@ export default {
  methods: {
    updatePlugins: function() {
      console.log("Updating plugin forms");
      axios
      return axios
        .get(this.pluginsUri)
        .then(response => {
          console.log(response);
@@ -258,6 +271,9 @@ export default {
      if (!(this.currentTab == tab)) {
        this.currentTab = tab;
      }
    },
    startModals: function() {
      this.$refs["calibrationModal"].show();
    }
  }
};
+270 −0
Original line number Diff line number Diff line
<template>
  <div id="modal-example" ref="calibrationModalEl" uk-modal>
    <div v-if="ready" class="uk-modal-dialog uk-modal-body">
      <h2 class="uk-modal-title">Microscope Calibration</h2>
      <div v-show="stepValue == 0">
        <p>
          <b
            >Some important microscope calibration data is currently missing.</b
          >
        </p>
        <p>
          Your microscope will still function, however some functionality will
          be limited, and image quality will likely suffer.
        </p>
        <p>
          <b>Click Next to begin microscope calibration.</b>
        </p>
      </div>

      <div v-show="stepValue == 1">
        <h3>Lens-shading</h3>
        <div v-if="isLSTCalibrated">
          <p>
            <b
              >Your lens-shading table has already been calibrated. Click Next
              to move on.</b
            >
          </p>
        </div>
        <div v-else>
          <p>
            <b
              >Follow the important steps below before starting lens-shading
              calibration!</b
            >
          </p>
          <ul class="uk-list uk-list-bullet">
            <li>Remove any samples from your microscope</li>
            <li>Ensure your illumination is on and properly fixed in place</li>
          </ul>

          <miniStreamDisplay
            v-if="stepValue == 1"
            class="mini-preview"
          ></miniStreamDisplay>

          <p>Once you're ready, click auto-calibrate.</p>

          <cameraCalibrationSettings
            :show-extra-settings="false"
          ></cameraCalibrationSettings>
        </div>
      </div>

      <div v-show="stepValue == 2">
        <h3>Camera-stage mapping</h3>
        <div v-if="isCSMCalibrated">
          <p>
            <b
              >Your camera-stage mapping has already been calibrated. Click Next
              to move on.</b
            >
          </p>
        </div>
        <div v-else>
          <p>
            <b
              >Follow the important steps below before starting camera-stage
              mapping calibration!</b
            >
          </p>
          <ul class="uk-list uk-list-bullet">
            <li>Insert a clearly visible sample to the microscope</li>
            <li>
              Ensure the sample is reasonably well centered on the microscope
              camera
            </li>
          </ul>

          <miniStreamDisplay
            v-if="stepValue == 2"
            class="mini-preview"
          ></miniStreamDisplay>

          <p>Once you're ready, click auto-calibrate.</p>

          <cameraStageMappingSettings
            :show-extra-settings="false"
          ></cameraStageMappingSettings>
        </div>
      </div>

      <div v-show="stepValue == 3">
        <p>
          <b>Calibration complete</b>
        </p>
        <p>
          Click Finish to return to your microscope, or Restart to re-run the
          calibration routine
        </p>
      </div>

      <p class="uk-text-right">
        <button
          v-show="stepValue == 0"
          class="uk-button uk-button-default uk-modal-close"
          type="button"
        >
          Cancel
        </button>
        <button
          v-show="stepValue == 3"
          class="uk-button uk-button-default"
          type="button"
          @click="stepValue = 0"
        >
          Restart
        </button>
        <button
          v-show="stepValue < 3"
          class="uk-button uk-button-primary uk-margin-left"
          type="button"
          @click="increment()"
        >
          Next
        </button>
        <button
          v-show="stepValue == 3"
          class="uk-button uk-button-primary uk-margin-left"
          type="button"
          @click="hide()"
        >
          Finish
        </button>
      </p>
    </div>
  </div>
</template>

<script>
import axios from "axios";

import cameraCalibrationSettings from "../viewComponents/settingsComponents/cameraCalibrationSettings.vue";
import cameraStageMappingSettings from "../viewComponents/settingsComponents/cameraStageMappingSettings.vue";
import miniStreamDisplay from "../viewComponents/miniStreamDisplay.vue";

export default {
  name: "CalibrationModal",

  components: {
    cameraCalibrationSettings,
    cameraStageMappingSettings,
    miniStreamDisplay
  },

  props: {
    availablePlugins: {
      type: Array,
      required: true
    }
  },

  data: function() {
    return {
      ready: false,
      stepValue: 0,
      settings: {}
    };
  },

  computed: {
    settingsUri: function() {
      return `${this.$store.getters.baseUri}/api/v2/instrument/settings`;
    },
    availablePluginTitles: function() {
      return this.availablePlugins.map(a => a.title);
    },
    isCSMCalibrated: function() {
      if (this.settings.extensions["org.openflexure.camera_stage_mapping"]) {
        return true;
      } else {
        return false;
      }
    },
    isLSTCalibrated: function() {
      if (
        this.settings.camera.picamera &&
        this.settings.camera.picamera.lens_shading_table
      ) {
        return true;
      } else {
        return false;
      }
    },
    canCSMCalibrated: function() {
      return this.availablePluginTitles.includes(
        "org.openflexure.camera_stage_mapping"
      );
    },
    canLSTCalibrated: function() {
      return this.availablePluginTitles.includes(
        "org.openflexure.calibration.picamera"
      );
    },
    isUseful: function() {
      var CSMUseful = this.canCSMCalibrated && !this.isCSMCalibrated;
      var LSTUseful = this.canLSTCalibrated && !this.isLSTCalibrated;
      return CSMUseful || LSTUseful;
    }
  },

  methods: {
    show: function() {
      // Get current settings
      this.getSettings().then(() => {
        // Check if this calibration wizard can actually do anything useful
        console.log(this.isUseful);
        if (this.isUseful) {
          this.ready = true;
          this.stepValue = 0;
          // Show the modal
          var el = this.$refs["calibrationModalEl"];
          this.showModalElement(el); // Calls the mixin
        }
      });
    },

    hide: function() {
      // Show the modal
      var el = this.$refs["calibrationModalEl"];
      this.hideModalElement(el); // Calls the mixin
      this.ready = false;
    },

    getSettings: function() {
      return axios
        .get(this.settingsUri)
        .then(response => {
          this.settings = response.data;
        })
        .catch(error => {
          this.modalError(error); // Let mixin handle error
        });
    },

    decrement: function() {
      if (this.stepValue > 0) {
        this.stepValue = this.stepValue - 1;
      }
    },

    increment: function() {
      // Upper bound on section number
      if (this.stepValue < 3) {
        this.stepValue = this.stepValue + 1;
        return true;
      }
    }
  }
};
</script>

<style scoped>
.mini-preview {
  width: 75%;
  margin-left: auto;
  margin-right: auto;
  border: 1px solid #555;
}
</style>
+69 −0
Original line number Diff line number Diff line
<template>
  <div
    id="stream-display"
    ref="streamDisplay"
    class="stream-display uk-width-1-1 uk-height-1-1 scrollTarget"
  >
    <img
      ref="click-frame"
      class="uk-align-center uk-margin-remove-bottom"
      :hidden="!streamEnabled"
      :src="streamImgUri"
      alt="Stream"
    />

    <div v-if="!streamEnabled" class="uk-height-1-1">
      <div v-if="$store.state.waiting" class="uk-position-center">
        <div uk-spinner="ratio: 4.5"></div>
      </div>

      <div v-else class="uk-position-center position-relative text-center">
        No active connection
      </div>
    </div>
  </div>
</template>

<script>
// Export main app
export default {
  name: "MiniStreamDisplay",

  data: function() {
    return {};
  },

  computed: {
    streamEnabled: function() {
      return (
        this.$store.getters.ready &&
        !this.$store.state.globalSettings.disableStream
      );
    },
    streamImgUri: function() {
      return `${this.$store.getters.baseUri}/api/v2/streams/mjpeg`;
    }
  },
  methods: {}
};
</script>

<style scoped lang="less">
.stream-display img {
  text-align: center;
  object-fit: contain;
}

.stream-display {
  width: 100%;
  height: 100%;
}

.position-relative {
  position: relative !important;
}

.text-center {
  text-align: center;
}
</style>
+120 −0
Original line number Diff line number Diff line
<template>
  <div>
    <!--Show auto calibrate if default plugin is enabled-->
    <div v-if="'recalibrate' in recalibrationLinks" class="uk-margin-small">
      <taskSubmitter
        :can-terminate="false"
        :requires-confirmation="true"
        :confirmation-message="
          'Start recalibration? This may take a while, and the microscope will be locked during this time.'
        "
        :submit-url="recalibrationLinks.recalibrate.href"
        :submit-label="'Auto-Calibrate'"
        @response="onRecalibrateResponse"
        @error="onRecalibrateError"
      >
      </taskSubmitter>
    </div>

    <div v-show="showExtraSettings" class="uk-child-width-expand" uk-grid>
      <div v-if="'flatten_lens_shading_table' in recalibrationLinks">
        <button
          class="uk-button uk-button-danger uk-width-1-1"
          @click="flattenLensShadingTableRequest"
        >
          Disable flat-field correction
        </button>
      </div>

      <div v-if="'delete_lens_shading_table' in recalibrationLinks">
        <button
          class="uk-button uk-button-danger uk-width-1-1"
          @click="deleteLensShadingTableRequest"
        >
          Adaptive flat-field correction
        </button>
      </div>
    </div>
  </div>
</template>

<script>
import axios from "axios";
import taskSubmitter from "../../genericComponents/taskSubmitter";

// Export main app
export default {
  name: "CameraCalibrationSettings",

  components: {
    taskSubmitter
  },

  props: {
    showExtraSettings: {
      type: Boolean,
      required: false,
      default: true
    }
  },

  data: function() {
    return {
      recalibrationLinks: {},
      isCalibrating: false
    };
  },

  computed: {
    pluginsUri: function() {
      return `${this.$store.getters.baseUri}/api/v2/extensions`;
    }
  },

  mounted() {
    this.updateRecalibrationLinks();
  },

  methods: {
    updateRecalibrationLinks: function() {
      axios
        .get(this.pluginsUri) // Get a list of plugins
        .then(response => {
          var plugins = response.data;
          var foundExtension = plugins.find(
            e => e.title === "org.openflexure.calibration.picamera"
          );
          // if AutocalibrationPlugin is enabled
          if (foundExtension) {
            // Get plugin action link
            this.recalibrationLinks = foundExtension.links;
          } else {
            this.recalibrationLinks = {};
          }
        })
        .catch(error => {
          this.modalError(error); // Let mixin handle error
        });
    },

    onRecalibrateResponse: function() {
      this.modalNotify("Finished recalibration.");
      // Update local settings
      this.updateSettings();
    },

    onRecalibrateError: function(error) {
      this.modalError(error); // Let mixin handle error
    },

    flattenLensShadingTableRequest: function() {
      axios.post(this.recalibrationLinks.flatten_lens_shading_table.href);
    },
    deleteLensShadingTableRequest: function() {
      axios.post(this.recalibrationLinks.delete_lens_shading_table.href);
    }
  }
};
</script>

<style lang="less"></style>
+6 −88
Original line number Diff line number Diff line
@@ -57,76 +57,37 @@
    </div>

    <!--Show auto calibrate if default plugin is enabled-->
    <div v-if="'recalibrate' in recalibrationLinks" class="uk-margin-small">
    <h3>Automatic calibration</h3>
      <taskSubmitter
        :can-terminate="false"
        :requires-confirmation="true"
        :confirmation-message="
          'Start recalibration? This may take a while, and the microscope will be locked during this time.'
        "
        :submit-url="recalibrationLinks.recalibrate.href"
        :submit-label="'Auto-Calibrate'"
        @response="onRecalibrateResponse"
        @error="onRecalibrateError"
      >
      </taskSubmitter>
    </div>

    <div class="uk-child-width-expand" uk-grid>
      <div v-if="'flatten_lens_shading_table' in recalibrationLinks">
        <button
          class="uk-button uk-button-danger uk-width-1-1"
          @click="flattenLensShadingTableRequest"
        >
          Disable flat-field correction
        </button>
      </div>

      <div v-if="'delete_lens_shading_table' in recalibrationLinks">
        <button
          class="uk-button uk-button-danger uk-width-1-1"
          @click="deleteLensShadingTableRequest"
        >
          Adaptive flat-field correction
        </button>
      </div>
    </div>
    <cameraCalibrationSettings></cameraCalibrationSettings>
  </div>
</template>

<script>
import axios from "axios";
import taskSubmitter from "../../genericComponents/taskSubmitter";
import cameraCalibrationSettings from "./cameraCalibrationSettings.vue";

// Export main app
export default {
  name: "CameraSettings",

  components: {
    taskSubmitter
    cameraCalibrationSettings
  },

  data: function() {
    return {
      settings: {},
      recalibrationLinks: {},
      isCalibrating: false
      settings: {}
    };
  },

  computed: {
    settingsUri: function() {
      return `${this.$store.getters.baseUri}/api/v2/instrument/settings`;
    },
    pluginsUri: function() {
      return `${this.$store.getters.baseUri}/api/v2/extensions`;
    }
  },

  mounted() {
    this.updateSettings();
    this.updateRecalibrationLinks();
  },

  methods: {
@@ -141,27 +102,6 @@ export default {
        });
    },

    updateRecalibrationLinks: function() {
      axios
        .get(this.pluginsUri) // Get a list of plugins
        .then(response => {
          var plugins = response.data;
          var foundExtension = plugins.find(
            e => e.title === "org.openflexure.calibration.picamera"
          );
          // if AutocalibrationPlugin is enabled
          if (foundExtension) {
            // Get plugin action link
            this.recalibrationLinks = foundExtension.links;
          } else {
            this.recalibrationLinks = {};
          }
        })
        .catch(error => {
          this.modalError(error); // Let mixin handle error
        });
    },

    applyConfigRequest: function() {
      console.log("Applying config to the microscope");
      var payload = {
@@ -188,31 +128,9 @@ export default {
        .catch(error => {
          this.modalError(error); // Let mixin handle error
        });
    },

    onRecalibrateResponse: function() {
      this.modalNotify("Finished recalibration.");
      // Update local settings
      this.updateSettings();
    },

    onRecalibrateError: function(error) {
      this.modalError(error); // Let mixin handle error
    },

    flattenLensShadingTableRequest: function() {
      axios.post(this.recalibrationLinks.flatten_lens_shading_table.href);
    },
    deleteLensShadingTableRequest: function() {
      axios.post(this.recalibrationLinks.delete_lens_shading_table.href);
    }
  }
};
</script>

<style lang="less">
.center-spinner {
  margin-left: auto;
  margin-right: auto;
}
</style>
<style lang="less"></style>
Loading