OpenC3/cosmos

View on GitHub
openc3-cosmos-init/plugins/packages/openc3-cosmos-tool-cmdtlmserver/src/tools/CmdTlmServer/TargetsTab.vue

Summary

Maintainability
Test Coverage
<!--
# Copyright 2022 Ball Aerospace & Technologies Corp.
# All Rights Reserved.
#
# This program is free software; you can modify and/or redistribute it
# under the terms of the GNU Affero General Public License
# as published by the Free Software Foundation; version 3 with
# attribution addendums as found in the LICENSE.txt
#
# 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 Affero General Public License for more details.

# Modified by OpenC3, Inc.
# All changes Copyright 2022, OpenC3, Inc.
# All Rights Reserved
#
# This file may also be used under the terms of a commercial license
# if purchased from OpenC3, Inc.
-->

<template>
  <v-card>
    <v-card-title>
      {{ data.length }} Targets
      <v-spacer />
      <v-tooltip bottom :disabled="enterprise && commandAuthority">
        <template v-slot:activator="{ on, attrs }">
          <!-- This is a little weird because it captures all the clicks -->
          <!-- including the clicks on the button so the tooltipHandler -->
          <!-- is also the button handler  -->
          <div v-on="on" v-bind="attrs" @click="tooltipHandler('takeAll')">
            <v-btn
              color="primary"
              class="mr-2"
              :disabled="!commandAuthority"
              data-test="take-all"
            >
              Take All Cmd Authority
              <v-icon right> mdi-account-check </v-icon>
            </v-btn>
          </div>
        </template>
        <span v-if="enterprise">
          Command Authority is disabled.<br />Click the button above to navigate
          to the Admin Console / Scopes Tab.
        </span>
        <span v-else>
          Command Authority is Enterprise Only.<br />Click the button above to
          learn more.
        </span>
      </v-tooltip>
      <v-tooltip bottom :disabled="enterprise && commandAuthority">
        <template v-slot:activator="{ on, attrs }">
          <div v-on="on" v-bind="attrs" @click="tooltipHandler('releaseAll')">
            <v-btn
              color="primary"
              class="mr-2"
              :disabled="!commandAuthority"
              data-test="release-all"
            >
              Release All Cmd Authority
              <v-icon right> mdi-account-cancel </v-icon>
            </v-btn>
          </div>
        </template>
        <span v-if="enterprise">
          Command Authority is disabled.<br />Click the button above to navigate
          to the Admin Console / Scopes Tab.
        </span>
        <span v-else>
          Command Authority is Enterprise Only.<br />Click the button above to
          learn more.
        </span>
      </v-tooltip>
      <v-text-field
        v-model="search"
        label="Search"
        prepend-inner-icon="mdi-magnify"
        clearable
        outlined
        dense
        single-line
        hide-details
        class="search"
      />
    </v-card-title>
    <v-data-table
      :headers="headers"
      :items="data"
      :search="search"
      :items-per-page="10"
      :footer-props="{ itemsPerPageOptions: [10, 20, -1] }"
      calculate-widths
      multi-sort
      data-test="targets-table"
    >
      <template v-slot:item.take="{ item }">
        <span v-if="item.name === 'UNKNOWN'">N/A</span>
        <v-btn
          v-if="item.name != 'UNKNOWN'"
          block
          color="primary"
          @click="take(item.name)"
          :disabled="!commandAuthority"
        >
          Take
          <v-icon right> mdi-account-check </v-icon>
        </v-btn>
      </template>
      <template v-slot:item.release="{ item }">
        <span v-if="item.name === 'UNKNOWN'">N/A</span>
        <v-btn
          v-if="item.name != 'UNKNOWN'"
          block
          color="primary"
          @click="release(item.name)"
          :disabled="!commandAuthority"
        >
          Release
          <v-icon right> mdi-account-cancel </v-icon>
        </v-btn>
      </template>
    </v-data-table>
    <upgrade-to-enterprise-dialog
      v-model="showUpgradeToEnterpriseDialog"
      reason="Command Authority is Enterprise Only"
    ></upgrade-to-enterprise-dialog>
  </v-card>
</template>

<script>
import Api from '@openc3/tool-common/src/services/api'
import Updater from './Updater'
import UpgradeToEnterpriseDialog from '@openc3/tool-common/src/components/UpgradeToEnterpriseDialog'
import Cable from '@openc3/tool-common/src/services/cable.js'

export default {
  components: {
    UpgradeToEnterpriseDialog,
  },
  mixins: [Updater],
  props: {
    tabId: Number,
    curTab: Number,
  },
  data() {
    return {
      search: '',
      data: [],
      cable: new Cable(),
      enterprise: false,
      headers: [
        { text: 'Target Name', value: 'name' },
        { text: 'Interfaces', value: 'interface' },
        {
          text: 'Command Authority Username',
          value: 'username',
        },
        { text: 'Take Command Authority', value: 'take' },
        { text: 'Release Command Authority', value: 'release' },
      ],
      cmdAuth: {},
      commandAuthority: false,
      showUpgradeToEnterpriseDialog: false,
    }
  },
  created: async function () {
    await Api.get('/openc3-api/info').then((response) => {
      if (response.data.enterprise) {
        this.enterprise = true
      }
    })
    // Populate the table once and then just update the data in the
    // update() method. This ensures the data doesn't get deleted
    // on each update which can break takeAll and releaseAll
    this.api.get_target_interfaces().then((info) => {
      for (let x of info) {
        this.data.push({
          name: x[0],
          interface: x[1],
          username: '',
        })
      }
    })
    if (this.enterprise) {
      // Get the initial scope setting
      Api.get(`/openc3-api/scopes/${window.openc3Scope}`).then((response) => {
        if (response.data.command_authority) {
          this.commandAuthority = true
        }
      })
      // Get the initial command authority settings
      Api.get('/openc3-api/cmdauth').then((response) => {
        this.cmdAuth = response.data
      })
      // Create a cable to the SystemEventsChannel so we can maintain
      // state with the backend
      this.cable
        .createSubscription('SystemEventsChannel', window.openc3Scope, {
          received: (data) => {
            this.cable.recordPing()
            this.handleMessages(data)
          },
        })
        .then((systemSubscription) => {
          this.systemSubscription = systemSubscription
        })
    }
  },
  destroyed() {
    if (this.systemSubscription) {
      this.systemSubscription.unsubscribe()
    }
    this.cable.disconnect()
  },
  methods: {
    // Enterprise only
    handleMessages(data) {
      data.forEach((message) => {
        let event = JSON.parse(message['event'])
        switch (event.type) {
          case 'scope':
            if (window.openc3Scope === event.name) {
              this.commandAuthority = event.command_authority
            }
            break
          case 'cmd_auth_take':
            this.cmdAuth[event.name] = event
            break
          case 'cmd_auth_release':
            delete this.cmdAuth[event.name]
            break
          // There is also 'role' but we don't care
        }
      })
    },
    tooltipHandler(method) {
      if (this.enterprise) {
        if (this.commandAuthority) {
          this[method]()
        } else {
          window.open('/tools/admin/scopes', '_blank')
        }
      } else {
        this.showUpgradeToEnterpriseDialog = true
      }
    },
    update() {
      if (this.tabId != this.curTab) return
      this.api.get_target_interfaces().then(async (info) => {
        for (let i = 0; i < info.length; i++) {
          this.data[i].name = info[i][0]
          this.data[i].interface = info[i][1]
          if (this.enterprise) {
            if (this.cmdAuth[this.data[i].name]) {
              this.data[i].username =
                this.cmdAuth[this.data[i].name]['username']
            } else {
              this.data[i].username = ''
            }
          }
        }
      })
    },
    take(target_name) {
      Api.post('/openc3-api/cmdauth/take', {
        data: {
          target_name: target_name,
        },
      })
    },
    release(target_name) {
      Api.post('/openc3-api/cmdauth/release', {
        data: {
          target_name: target_name,
        },
      })
    },
    takeAll() {
      Api.post('/openc3-api/cmdauth/take-all')
    },
    releaseAll() {
      Api.post('/openc3-api/cmdauth/release-all')
    },
  },
}
</script>