<template>
  <el-container>
    <el-aside width="15%">
      <div class="menu-box">
        <el-card class="box-card">
          <div slot="header" class="menu-header">
            <div class="header-title">设备</div>
            <el-dropdown size="small" style="vertical-align: middle" trigger="click" @command="handleCommand">
              <el-button type="primary" round>
                <i class="el-icon-s-tools" style="font-size: 14px"></i><i class="el-icon-arrow-down el-icon--right"
                  style="font-size: 14px"></i>
              </el-button>
              <el-dropdown-menu slot="dropdown">
                <el-dropdown-item command="add">新加设备</el-dropdown-item>
                <el-dropdown-item command="del">删除设备</el-dropdown-item>
                <el-dropdown-item>设备重启</el-dropdown-item>
                <el-dropdown-item>
                  <el-upload
                    class="upload-demo"
                    ref="upload"
                    action=""
                    :before-upload="importDeviceConfigure">
                    导入配置
                  </el-upload>
                </el-dropdown-item>
                <el-dropdown-item command="export" >导出配置</el-dropdown-item>
              </el-dropdown-menu>
            </el-dropdown>
          </div>

          <el-menu :style="{ borderRight: 'none' }" :defaultActive="deviceActiveStatus" @select="getDeviceConfigure">
            <el-menu-item class="serve-item" shadow="always" v-for="(item, i) in deviceData" :key="i" :index="i + ''">
              <div style="position: relative">
                <div style="display: inline-block">{{ item.name }}</div>
                <div :class="{
                  'device-status': true,
                  'device-status-online': item.status,
                  'device-status-offline': !item.status,
                }"></div>
              </div>
            </el-menu-item>
          </el-menu>
        </el-card>
      </div>
    </el-aside>
    <el-main :style="{ padding: '10px' }">
      <el-card class="box-card">
        <div slot="header" style="position: relative">
          <div class="header-title">设备配置</div>
          <el-button class="header-button" type="primary" @click="setDeviceConfigureConfirm">应用</el-button>
        </div>
        <device-configure ref="deviceConfigure" :defaultDeviceName="defaultDeviceName"
          :defaultFormData="defaultFormData" />
      </el-card>
      <el-card class="box-card" style="margin-top: 5px">
        <d2-crud ref="d2Crud"
          :columns="columns"
          :data="devicePointTableData"
          :rowHandle="rowHandle"
          add-title="新增"
          edit-title="绑定"
          :add-template="addTemplate"
          :add-rules="addRules"
          :form-options="formOptions"
          @row-add="addDevicePoint"
          @row-edit="devicePointBandingNode"
          @row-remove="delDevicePoint"
          @cpoy='copyDevicePoint'
          @dialog-cancel="handleDialogCancel"
          @banding="bandingNodeTemplate"
          @cell-data-change="handleCellDataChange"
          @form-data-change="handleFormDataChange"
          >
          <div slot="header">
            <el-button  style="margin-bottom: 5px" @click="addRow">新增</el-button>
            <span class="demo-form-inline" style="margin-left:15px;">
              筛选：<el-input placeholder="请输入筛选内容" v-model="nameSearchValue" style="width:300px" class="input-with-select"></el-input>
            </span>
          </div>

        </d2-crud>
      </el-card>
    </el-main>
  </el-container>
</template>

<script>
import { each, assign, unset, filter, isArray, map, capitalize, split, toUpper, replace } from 'lodash'
const DataTypeCodeOptions = [
  {
    value: 'string',
    label: 'string (字符串)'
  },
  {
    value: 'int',
    label: 'int (整数)'
  },
  {
    value: 'float',
    label: 'float (浮点数)'
  },
  {
    value: 'bool',
    label: 'bool (逻辑值)'
  },
  {
    value: 'uint',
    label: 'uint'
  },
  {
    value: 'short',
    label: 'short'
  },
  {
    value: 'ushort',
    label: 'ushort'
  },
  {
    value: 'long',
    label: 'long'
  },
  {
    value: 'ulong',
    label: 'ulong'
  },
  {
    value: 'double',
    label: 'double'
  },
  {
    value: 'byte',
    label: 'byte'
  },
  {
    value: 'sbyte',
    label: 'sbyte'
  }
]

export default {
  props: {
    server: {
      default: {
        id: 0,
        url: '',
        port: ''
      }
    }
  },
  components: {
    DeviceConfigure: () => import('./deviceConfigure')
  },
  data () {
    return {
      active: '',
      columns: [
        {
          title: '序号',
          key: 'id'
        },
        {
          title: '名称',
          key: '@Name',
          'show-overflow-tooltip': true,
          component: {
            size: 'small'
          }
        },
        {
          title: '绑定',
          key: '@Binding'
        },
        {
          title: '地址',
          key: '@Address',
          component: {
            name: 'el-input',
            size: 'small'
          }
        },
        {
          title: '类型',
          key: '@DataTypeCode',
          component: {
            name: 'el-select',
            size: 'small',
            options: DataTypeCodeOptions
          }
        },
        {
          title: '采集周期',
          key: '@RequestInterval',
          component: {
            name: 'el-input',
            size: 'small'
          }
        },
        {
          title: '长度',
          key: '@Length',
          width: '80%',
          component: {
            name: 'el-input',
            size: 'small'
          }
        },
        {
          title: '说明',
          key: '@Description',
          component: {
            name: 'el-input',
            size: 'small'
          }
        },
        {
          title: '当前值',
          key: '@Value',
          width: '80%',
          component: {
            name: 'el-input',
            size: 'small'
          }
        }
      ],
      rowHandle: {
        minWidth: '150',
        custom: [
          {
            text: '绑定',
            size: 'mini',
            emit: 'banding',
            show (index, row) {
              if (row.showBindButton) {
                return true
              }
              return false
            }
          },
          {
            text: '复制',
            size: 'mini',
            emit: 'cpoy',
            show (index, row) {
              if (row.showCopyButton) {
                return true
              }
              return false
            }
          }
        ],
        remove: {
          size: 'mini',
          confirm: true,
          show (index, row) {
            if (row.showRemoveButton) {
              return true
            }
            return false
          }
        }
      },
      formOptions: {
        modal: false,
        saveLoading: false
      },
      addTemplate: {
        '@Name': {
          title: '名称'
        },
        '@Address': {
          title: '地址'
        },
        '@DisplayName': {
          title: '显示名称'
        },
        '@RequestType': {
          title: '请求类型'
        },
        '@DataTypeCode': {
          title: '数据类型',
          component: {
            name: 'el-select',
            options: DataTypeCodeOptions,
            span: 12
          }
        },
        '@RequestInterval': {
          title: '采集周期(ms)'
        },
        '@Length': {
          title: '长度'
        },
        '@Description': {
          title: '说明'
        }
      },
      addRules: {
        '@Name': [
          {
            required: true,
            type: 'string',
            message: '请输入名称',
            trigger: 'blur'
          }
        ],
        '@Address': [
          {
            required: true,
            type: 'string',
            message: '请输入地址',
            trigger: 'blur'
          }
        ],
        '@DisplayName': [
          {
            required: true,
            type: 'string',
            message: '请输入显示名称',
            trigger: 'blur'
          }
        ],
        '@RequestType': [
          {
            required: true,
            type: 'string',
            message: '请输入请求类型',
            trigger: 'blur'
          }
        ],
        '@DataTypeCode': [
          {
            required: true,
            type: 'string',
            message: '请选择类型',
            trigger: 'blur'
          }
        ],
        '@RequestInterval': [
          { required: true, message: '请输入采集周期(ms)', trigger: 'blur' }
        ]
      },
      timing: null,
      nameSearchValue: '',
      serverData: {},
      devicePointData: [],
      nodeCodeData: [],
      workingSubclasses: [],
      dialogVisible: false,
      deviceActiveStatus: '0',
      defaultDeviceName: '',
      defaultFormData: {},
      deviceData: [],
      deviceConfigure: {}
    }
  },
  watch: {
    server: {
      handler (val) {
        this.serverData = val
        if (val.id) {
          this.getDevice()
          this.getDeviceStatus()
        }
      },
      immediate: true
    }
  },
  computed: {
    devicePointTableData () {
      if (this.nameSearchValue) {
        return this.devicePointData.filter((item) => {
          if (item['@Name'].search(this.nameSearchValue) > -1) {
            return item
          }
        })
      } else {
        return this.devicePointData
      }
    }
  },
  methods: {
    addRow () {
      this.$refs.d2Crud.showDialog({
        mode: 'add'
      })
    },
    handleFormDataChange ({ key }) {
      if (key === 'workingSubclass') {
        const { workingSubclass } = this.$refs.d2Crud.formData
        this.getCodesByWorkingSubclass(workingSubclass)
        this.$refs.d2Crud.$forceUpdate()
      }
    },
    handleCellDataChange ({ rowIndex, row, key, value }) {
      let type
      if (row['@DataTypeCode'][0] === 'u') {
        type = split(row['@DataTypeCode'], '')
        type[1] = toUpper(type[0] + type[1])
        type = type.join('')
      } else {
        type = capitalize(row['@DataTypeCode'])
      }
      if (key === '@Value') {
        this.$api.SET_DEVICE_POINT_VALUE(
          'http://' + this.serverData.url + ':' + this.serverData.port,
          this.deviceData[this.deviceActiveStatus].name,
          row['@Address'],
          type,
          value
        ).then(res => {
          if (!res.IsSuccess) {
            this.$message({
              message: res.Message,
              type: 'error'
            })
          }
        })
      }
      this.devicePointData[rowIndex] = row
    },
    bandingNodeTemplate ({ index }) {
      this.$refs.d2Crud.showDialog({
        mode: 'edit',
        rowIndex: index,
        template: {
          workingSubclass: {
            title: '工序编码',
            value: '',
            component: {
              name: 'el-select',
              options: this.workingSubclasses,
              span: 12
            }
          },
          nodeCode: {
            title: '节点名称',
            value: '',
            component: {
              name: 'el-select',
              options: this.nodeCodeData,
              span: 12
            }
          }
        }
      })
    },
    handleDialogCancel (done) {
      this.$message({
        message: '用户取消保存',
        type: 'warning'
      })
      done()
    },
    handleCommand (command) {
      switch (command) {
        case 'add':
          this.addDevice()
          break
        case 'del':
          this.delDevice()
          break
        case 'export':
          this.exportDeviceConfigure()
          break
      }
    },
    async getCodesByWorkingSubclass (workingSubclass) {
      try {
        const nodeCode = await this.$api.QUERY_CODES(workingSubclass)
        const nodeCodeData = []
        each(nodeCode, (o) => {
          nodeCodeData.push({ value: o, label: o })
        })
        this.nodeCodeData = nodeCodeData
        this.$nextTick(() => {
          this.$refs.d2Crud.handleFormTemplateMode(
            'nodeCode'
          ).component.options = this.nodeCodeData
          this.$refs.d2Crud.$forceUpdate()
        })
      } catch (e) {
        console.log(e)
      }
    },
    async getworkingSubclasses () {
      try {
        const workingSubclasses = await this.$api.QUERY_WORKING_SUBCLASSES()
        const workingSubclassesData = []
        each(workingSubclasses, (o) => {
          workingSubclassesData.push({ value: o, label: o })
        })
        this.workingSubclasses = workingSubclassesData
      } catch (e) {
        console.log(e)
      }
    },
    async getDevice () {
      try {
        this.deviceData = await this.$api.GET_DEVICE(this.serverData.id)
        if (this.deviceData.length > 0) {
          this.getDeviceConfigure(this.deviceActiveStatus)
        } else {
          this.devicePointData = [] // 如果没有设备，防止显示上一次打开的设备数据先清空数据
          this.defaultDeviceName = ''
          this.nameSearchValue = ''
        }
      } catch (e) {
        console.log(e)
      }
    },
    async getDeviceConfigure (e) {
      this.deviceActiveStatus = e
      this.devicePointData = [] // 当切换设备时把保存configure、point的数据清空
      this.defaultDeviceName = ''
      this.nameSearchValue = ''
      const deviceData = await this.$api.GET_DEVICE_CONFIGURE(
        this.deviceData[e].id
      )
      if (deviceData[0].conf) {
        const conf = JSON.parse(deviceData[0].conf)
        if (conf.DeviceTypeName !== undefined) {
          this.defaultDeviceName = conf.DeviceTypeName
          this.defaultFormData = conf
        }
        // 获取point信息
        const devicePointAddress = []
        if (conf.RequestNode !== undefined && conf.RequestNode.length > 0) {
          this.devicePointData = each(conf.RequestNode, (o, i) => {
            let type
            if (o['@DataTypeCode'][0] === 'u') {
              type = split(o['@DataTypeCode'], '')
              type[1] = toUpper(type[0] + type[1])
              type = type.join('')
            } else {
              type = capitalize(o['@DataTypeCode'])
            }
            devicePointAddress.push({
              key: i,
              value: o['@Address'],
              type:
                o['@DataTypeCode'][0] === 'u'
                  ? replace(o['@DataTypeCode'])
                  : capitalize(o['@DataTypeCode'])
            })
            assign(o, {
              id: i + 1,
              showBindButton: true,
              showCopyButton: true,
              showRemoveButton: true
            })
          })
          if (devicePointAddress.length > 0) {
            this.getDevicePoint(devicePointAddress)
          }
        }
      }
    },
    addDevice () {
      this.$prompt('输入设备名称', '新加设备', {
        confirmButtonText: '确定',
        inputPattern: /^[\s\S]*.*[^\s][\s\S]*$/,
        inputErrorMessage: '请输入设备名称'
      }).then(async ({ value }) => {
        await this.$api.ADD_DEVICE({
          action: 'add_device',
          name: value,
          server_id: this.serverData.id
        })
        this.getDevice()
        this.$message({
          message: '新加设备成功',
          type: 'success'
        })
      })
    },
    async delDevice () {
      const deviceConfigure = await this.$api.GET_HSLSERVER_CONFIGURE(
        'http://' + this.serverData.url + ':' + this.serverData.port
      )
      let deviceNode = deviceConfigure.Content.Settings.GroupNode[0].DeviceNode
      if (deviceNode !== undefined && isArray(deviceNode)) {
        deviceNode = filter(deviceNode, (item) => {
          return item['@Name'] === this.deviceData[this.deviceActiveStatus].name
            ? null
            : item
        })
        deviceConfigure.Content.Settings.GroupNode[0].DeviceNode = deviceNode
      } else {
        unset(deviceConfigure, 'Content.Settings.GroupNode[0].DeviceNode')
      }
      try {
        await this.$api.SET_HSLSERVER_CONFIGURE(
          'http://' + this.serverData.url + ':' + this.serverData.port,
          { data: deviceConfigure.Content }
        )
        await this.$api.DEL_DEVICE({
          action: 'remove_device',
          id: this.deviceData[this.deviceActiveStatus].id
        })
        this.$message({
          message: '删除设备成功',
          type: 'success'
        })
        this.getDevice()
      } catch (e) {
        console.log(e)
      }
    },
    async addDevicePoint (row, done) {
      this.formOptions.saveLoading = true
      if (!this.$refs.deviceConfigure.defaultDeviceTypeNameValue) {
        this.$message({
          message: '请先应用设备配置',
          type: 'error'
        })
        done(false)
      } else {
        if (row['@DataTypeCode'] === 'string' && !row['@Length']) {
          this.$message({
            message: '数据类型为string，长度不能为空',
            type: 'error'
          })
          this.formOptions.saveLoading = false
          return false
        }
        assign(row, {
          id: this.devicePointData.length + 1,
          showBindButton: true,
          showCopyButton: true,
          showRemoveButton: true
        })
        this.devicePointData.push(row)
        done()
      }
      this.formOptions.saveLoading = false
    },
    async delDevicePoint ({ index, row }, done) {
      this.$delete(this.devicePointData, index)
      done()
    },
    getDevicePoint (devicePointAddress) {
      clearInterval(this.timing) // 先关闭上一个定时器
      this.timing = setInterval(() => {
        each(devicePointAddress, async (item) => {
          this.$api
            .GET_DEVICE_POINT_VALUE(
              'http://' + this.serverData.url + ':' + this.serverData.port,
              this.deviceData[this.deviceActiveStatus].name,
              item.value,
              item.type
            )
            .then((res) => {
              this.$set(
                this.devicePointData[item.key],
                '@Value',
                res.Content
              )
            })
        })
      }, 10000)
    },
    async devicePointBandingNode ({ index, row }, done) {
      this.$delete(this.devicePointData[index], '@Binding')
      this.$set(this.devicePointData[index], '@Binding', row.nodeCode)
      done()
    },
    async copyDevicePoint ({ row }) {
      this.$prompt('复制该点位前，请输入设备点位名称', '复制点位', {
        confirmButtonText: '确定',
        inputPattern: /^[\s\S]*.*[^\s][\s\S]*$/,
        inputErrorMessage: '请输入设备点位名称'
      }).then(async ({ value }) => {
        row.id = this.devicePointData.length + 1
        row['@Name'] = value
        this.devicePointData.push(row)
      })
    },
    setDeviceConfigureConfirm () {
      this.$confirm('是否要应用该配置信息，更改配置后需要重启改服务才能使配置生效?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.setDeviceConfigure()
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消应用设备配置'
        })
      })
    },
    async setDeviceConfigure () {
      try {
        // 验证表单
        this.$refs.deviceConfigure.$refs.form.validate((valid) => {
          if (!valid) {
            return false
          }
        })
        const deviceConfigure = await this.$api.GET_HSLSERVER_CONFIGURE(
          'http://' + this.serverData.url + ':' + this.serverData.port
        )
        const deviceConfigureModelValue = this.$refs.deviceConfigure.deviceConfigureModelValue
        if (this.devicePointData.length > 0) {
          let devicePointData = this.devicePointData
          devicePointData = each(devicePointData, (item) => {
            if (item['@DataTypeCode'] !== 'string') {
              unset(item, '@Length')
            }
            unset(item, 'id')
            unset(item, 'showBindButton')
            unset(item, 'showCopyButton')
            unset(item, 'showRemoveButton')
            unset(item, '@Value')
            item['@Binding'] = item['@Binding'] || ''
          })
          deviceConfigureModelValue.RequestNode = devicePointData
        }
        deviceConfigureModelValue['@Name'] = this.deviceData[this.deviceActiveStatus].name
        let deviceNode = deviceConfigure.Content.Settings.GroupNode[0].DeviceNode || []
        let isExist = false
        if (deviceNode !== undefined) {
          deviceNode = isArray(deviceNode) ? deviceNode : [deviceNode]
          deviceNode = map(deviceNode, (item) => {
            if (
              item['@Name'] === this.deviceData[this.deviceActiveStatus].name
            ) {
              isExist = true
              item = deviceConfigureModelValue
            }
            return item
          })
        }

        if (!isExist) {
          deviceNode.push(deviceConfigureModelValue)
        }
        deviceConfigure.Content.Settings.GroupNode[0].DeviceNode = deviceNode
        await this.$api.SET_HSLSERVER_CONFIGURE(
          'http://' + this.serverData.url + ':' + this.serverData.port,
          { data: deviceConfigure.Content }
        )
        deviceConfigureModelValue.DeviceTypeName =
          this.$refs.deviceConfigure.defaultDeviceTypeNameValue
        const data = {
          action: 'update_device',
          conf: JSON.stringify(deviceConfigureModelValue),
          id: this.deviceData[this.deviceActiveStatus].id
        }
        this.$api.SET_DEVICE_CONFIGURE(data)
        this.$message({
          message: '操作成功',
          type: 'success'
        })
        this.$emit('changeServerConfig', 't')
      } catch (e) {
        console.log(e)
      }
    },
    async getDeviceStatus () {
      const res = await this.$api.GET_SERVER_DEVICE_STATUS(
        'http://' + this.serverData.url + ':' + this.serverData.port
      )
      if (res) {
        this.$emit('changeStatus', 'online')
        const deviceStatus = {}
        each(res.Content.__deviceList, (item) => {
          deviceStatus[item.__name] = {
            status: item.__requestEnable,
            name: item.__name
          }
        })
        each(this.deviceData, (item) => {
          if (deviceStatus[item.name]) {
            item.status = deviceStatus[item.name].status
          }
        })
      } else {
        this.$emit('changeStatus', false)
      }
    },
    exportDeviceConfigure () {
      const deviceConfigure = this.$refs.deviceConfigure.deviceConfigureModelValue
      const devicePointData = this.devicePointData
      deviceConfigure.DeviceNode = devicePointData
      deviceConfigure.DeviceTypeName = this.$refs.deviceConfigure.defaultDeviceTypeNameValue
      const deviceData = JSON.stringify(deviceConfigure)
      const exportLink = document.createElement('a')
      const blob = new Blob([deviceData], { type: 'text/json' })
      exportLink.href = URL.createObjectURL(blob)
      exportLink.download = deviceConfigure['@Name'] + '.json'
      exportLink.style.display = 'none'
      document.body.appendChild(exportLink)
      exportLink.click()
      exportLink.remove()
    },
    importDeviceConfigure (file) {
      const reader = new FileReader()
      reader.readAsText(file, 'UTF-8')
      const that = this
      reader.onload = function (e) {
        const fileData = e.target.result
        const deviceConfigureData = JSON.parse(fileData)
        that.devicePointData = deviceConfigureData.DeviceNode
        unset(deviceConfigureData, 'DeviceNode')
        that.defaultFormData = deviceConfigureData
        that.defaultDeviceName = deviceConfigureData.DeviceTypeName
      }
      return false
    },
    clearTime () {
      clearInterval(this.timing)
      this.timing = null
    }
  },
  mounted () {
    this.getworkingSubclasses()
  }
}
</script>

<style scoped>
.menu-box {
  box-sizing: border-box;
  padding: 10px;
  vertical-align: middle;
}

.el-dropdown {
  vertical-align: top;
}

.el-dropdown+.el-dropdown {
  margin-left: 15px;
}

.el-icon-arrow-down {
  font-size: 12px;
}

.menu-header {
  position: relative;
}

.header-title {
  width: 60%;
  display: inline-block;
  font-weight: bold;
  font-size: 20px;
  color: black;
  vertical-align: middle;
}

.header-button {
  float: right;
  vertical-align: middle;
}

.serve-item {
  border: none;
  margin-bottom: 10px;
  box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
  overflow: hidden;
  outline: 0;
  border-radius: 4px;
}

.device-status {
  width: 10px;
  height: 10px;
  border-radius: 50%;

  right: 0;
  position: absolute;
  top: 50%;
  transform: translate(0, -50%);
}

.device-status-online {
  background-color: #67c23a;
}

.device-status-offline {
  background-color: #ccc;
}
</style>
