123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692 |
- <template>
- <div class="g-p20 g-mb-40 content">
- <base-head :titles="titles.arr" @onTabChange="change" bottom="20"></base-head>
- <div class="g-card g-p20 g-fx main" v-loading="dataSource.loading">
- <div class="left">
- <div class="left__head">
- <div class="info">
- <div class="info1">基本信息</div>
- <div class="info2">{{ equipmentInfo.name }}</div>
- <div class="info3">
- <span>设备序列号 </span> <span class="g-ml-20">{{ equipmentInfo.code }}</span>
- </div>
- <div class="info3">
- <span>监控编号</span>
- <span class="g-ml-20">
- {{ equipmentInfo.productNo }}
- </span>
- </div>
- <div class="info3">
- <span>设备型号</span> <span class="g-ml-20">{{ equipmentInfo.model }}</span>
- </div>
- </div>
- <div class="info__img" :class="{ isOnLine: !equipmentInfo?.isOnline }"></div>
- <div class="head__second">
- <div class="second-info">
- <div class="second-name">
- {{ '报警 ' + alarmList.array.length }}
- </div>
- <div class="second-alarm"><live-message :list="alarmList.array"></live-message></div>
- </div>
- <div class="second-info">
- <div class="second-name bg2">
- {{ '预警 ' + warningList.array.length }}
- </div>
- <div class="second-warning"><live-message :list="warningList.array"></live-message></div>
- </div>
- </div>
- </div>
- <div class="left__footer g-p20">
- <div class="footer__info g-fx">
- <div class="item" v-if="currentData?.stroke">
- <div>
- <div class="text1">
- {{ formatValue(currentData?.stroke.value, currentData?.stroke.translate) }}
- <span style="font-weight: 400; font-size: 16px">{{ currentData?.stroke.unit }}</span>
- </div>
- <div class="text2">
- {{ currentData?.isHoist ? '高度' : '行程' }}
- </div>
- </div>
- </div>
- <div class="item" v-if="currentData?.gear">
- <div>
- <div class="text1">
- {{ formatGears(currentData?.gear) }}
- <span style="font-weight: 400; font-size: 16px">{{ currentData?.gear.unit }}</span>
- </div>
- <div class="text2">
- {{ '档位' }}
- </div>
- </div>
- </div>
- <div class="item" v-if="currentData?.weight">
- <div>
- <div class="text1">
- {{ formatValue(currentData?.weight.value, currentData?.weight.translate) }}
- <span style="font-weight: 400; font-size: 16px">{{ currentData?.weight.unit }}</span>
- </div>
- <div class="text2">
- {{ '载荷' }}
- <div class="weight-color" :style="{ backgroundColor: formatWeightLight(currentData?.weight.light) }">
- </div>
- </div>
- </div>
- </div>
- </div>
- <!-- 中间六个指示灯 -->
- <div class="g-fx footer__info2">
- <div class="item2" v-for="el in currentData?.middleLightList" :key="el.variableId">
- <div class="text1" :style="{ backgroundColor: getBgColor(el) }"></div>
- <div class="text2">{{ el.variableName }}</div>
- </div>
- </div>
- </div>
- </div>
- <div class="right">
- <div class="g-fx-j right__item" v-for="(el, i) in currentData?.switchVariables" :key="i">
- <div class="text">{{ el.variableName }}</div>
- <div class="text2">
- <div :style="{ backgroundColor: getBgColor(el) }"></div>
- </div>
- </div>
- <div class="g-fx-j right__item" v-for="(el, i) in currentData?.matchVariables" :key="i">
- <div class="text">{{ el.name }}</div>
- <div class="text2">
- {{ formatValue(el.vValue, el.translate) + el.unitName }}
- <div :style="{ backgroundColor: getMatchBgColor(el) }"></div>
- </div>
- </div>
- <div class="g-fx-j right__item" v-for="(el, i) in currentData?.valueVariables" :key="i">
- <div class="text">{{ el.variableName }}</div>
- <div class="text2">
- {{ formatValue(el.value, el.translate) + el.unitName }}
- </div>
- </div>
- </div>
- </div>
- </div>
- </template>
- <script setup lang="ts">
- import { getGroupAndVariables } from '@/api'
- import { fetchDeviceInfoAll } from '@/api/deviceAuthorize'
- import { useSidebarStore } from '@/store/sidebar'
- import { formatGears, formatValue, formatWeightLight } from '@/utils/tools'
- import LiveMessage from './live-message.vue'
- /** 警告数组 */
- const warningList: any = reactive({ array: [] })
- /** 报警数组 */
- const alarmList: any = reactive({ array: [] })
- let titles = reactive({
- arr: []
- })
- const sidebar = useSidebarStore()
- let equipmentInfo: any = ref({})
- let deviceId: any = ref('')
- let currentData: any = ref({})
- let dataSource: any = reactive({
- data: []
- })
- onUnmounted(() => {
- onUnsubscribe(`Byte/${equipmentInfo.value.code}`)
- })
- onMounted(async () => {
- const eId = useQuery('id')
- sidebar.setEquipmentId(eId)
- deviceId.value = useQuery('id') as string
- getDeviceInfo()
- getDataSource()
- })
- const getDeviceInfo = async () => {
- const data = await fetchDeviceInfoAll(deviceId.value)
- equipmentInfo.value = data
- onSubscribe([`Byte/${equipmentInfo.value.code}`],(topic: any, payload: any) => {
- parseData(payload)
- })
- }
- const getDataSource = async () => {
- const data = await getGroupAndVariables({ deviceId: deviceId.value })
- dataSource.data = data
- titles.arr = dataSource.data.map((item: { groupName: any }) => item.groupName)
- dataSource.data.forEach((group: any) => {
- group.middleLightList = group.switchVariables.slice(0, 6)
- group.switchVariables = group.switchVariables.slice(6)
- var light = group.weight?.light
- if (light != null) {
- updateAlarm('red', group.groupName + '超载', light.value)
- }
- group.middleLightList.forEach((g: any) => {
- updateAlarm(g.trueLight, group.groupName + g.variableName, g.value)
- })
- group.switchVariables.forEach((g: any) => {
- updateAlarm(g.trueLight, group.groupName + g.variableName, g.value)
- })
- })
- currentData.value = dataSource.data[0]
- }
- const change = (v: any, position: any) => {
- currentData.value = dataSource.data[position]
- }
- const parseData = (payload: any) => {
- const obj = JSON.parse(payload)
- //设备号
- const station = obj.Station
- //数据
- const byteArray = base64ToArray(obj.Data);
- for (var index = 0; index < dataSource.data.length; index++) {
- const item = dataSource.data[index]
- if (station != item.station) {
- continue
- }
- //载荷
- var weight = item.weight
- if (weight != null) {
- weight.value = toData(byteArray, weight.dataType, weight.address)
- var light = weight.light
- if (light != null) {
- light.value = light.isReverse != toData(byteArray, 1, light.address)
- updateAlarm('red', item.groupName + '超载', light.value)
- }
- }
- //高度或者 行程
- var stroke = item.stroke
- if (stroke != null) {
- var value = toData(byteArray, stroke.dataType, stroke.address)
- stroke.value = value < 0 ? '0' : value
- }
- //档位
- var gear = item.gear
- if (gear != null) {
- var array: any = []
- gear.options.forEach((option: any) => {
- var gearName = option.displayName
- var address = option.address
- var b = toData(byteArray, option.dataType, address)
- if (b) {
- array.push(gearName)
- }
- })
- gear.gears = array
- }
- item.middleLightList.forEach((m: any) => {
- m.value = m.isReverse != toData(byteArray, 1, m.address)
- updateAlarm(m.trueLight, item.groupName + m.variableName, m.value)
- })
- item.switchVariables.forEach((s: any) => {
- s.value = s.isReverse != toData(byteArray, 1, s.address)
- updateAlarm(s.trueLight, item.groupName + s.variableName, s.value)
- })
- item.valueVariables.forEach((e: any) => {
- const value = toData(byteArray, e.dataType, e.address)
- e.value = evaluateFormula(formatValue(value, e.translate), e.formula)
- })
- item.matchVariables.forEach((m: any) => {
- m.vValue = toData(byteArray, m.dataType, m.vAddress)
- var sAddress = m.sAddress
- if (sAddress.indexOf(',') > 0) {
- var addressArray = sAddress.split(',')
- for (var index = 0; index < addressArray.length; index++) {
- const addr = addressArray[index]
- var b = m.isReverse != toData(byteArray, 1, addr)
- if (b) {
- m.sValue = true
- updateAlarm(m.trueLight, item.groupName + m.sName, true)
- break
- }
- }
- m.sValue = false
- } else {
- m.sValue = m.isReverse != toData(byteArray, 1, sAddress)
- updateAlarm(m.trueLight, item.groupName + m.sName, m.sValue)
- }
- })
- }
- }
- const evaluateFormula = (value: any, formula: any) => {
- if (formula == null || formula == undefined || formula === '') {
- return value
- }
- //函数模板
- const array = formula.split(",")
- //字符串模本
- var templete = array[0]
- const formulaArray = array.slice(1)
- formulaArray.forEach((item: any, index: number) => {
- const func = item.replace("{Value}", value)
- const result = eval(func)
- templete = templete.replace(`{${index}}`, Math.floor(parseFloat(result)))
- })
- return templete
- }
- const base64ToArray = (str: string) => {
- const base64Str = window.atob(str);
- const len = base64Str.length;
- const bytes = new Uint8Array(len)
- for (var i = 0; i < len; i++) {
- bytes[i] = base64Str.charCodeAt(i)
- }
- return bytes;
- }
- const toData = (bytes: any, dataType: number, address: string) => {
- switch (dataType) {
- case 1: //布尔值
- var positionArray = address.split('.')
- if (positionArray.length > 0) {
- //字节位置
- var groupPosition = parseInt(positionArray[0])
- //值
- var childPosition = parseInt(positionArray[1])
- if (groupPosition <= bytes.length - 1) {
- return byte2Boolen(bytes[groupPosition])[childPosition]
- }
- }
- return ''
- case 2: //16-bit Unsigned
- var position = parseInt(address)
- return toUInt16(bytes, position)
- case 3: //16-bit Signed
- var position = parseInt(address)
- return toInt16(bytes, position)
- case 4: //32-bit Unsigned
- case 5:
- var position = parseInt(address)
- return toUInt32(bytes, position)
- case 6: //32-bit Float
- var position = parseInt(address)
- return toUInt32(bytes, position) << 0
- default:
- return ''
- }
- }
- const byte2Boolen = (byte: any) => {
- var bin: any = []
- byte
- .toString(2)
- .padStart(8, '0')
- .split('')
- .reverse()
- .forEach((c: any) => {
- bin.push(c == '1' ? true : false)
- })
- return bin
- }
- const toUInt16 = (bytes: any, offset: number) => {
- if (bytes.length < 2) return 0
- return parseInt(bytes[offset + 1].toString(16) + bytes[offset].toString(16), 16)
- }
- const toInt16 = (bytes: any, offset: number) => {
- var num = toUInt16(bytes, offset)
- return num > 32767 ? num - 65536 : num
- }
- const toUInt32 = (bytes: any, offset: number) => {
- if (bytes.length < 4) return 0
- var num = parseInt(
- bytes[offset + 1].toString(16) +
- bytes[offset + 0].toString(16) +
- bytes[offset + 3].toString(16) +
- bytes[offset + 2].toString(16),
- 16
- )
- return num
- }
- const updateAlarm = (trueLight: string, name: string, value: any) => {
- if (value == true || value == 'true') {
- if (trueLight == 'red') {
- var alarm = alarmList.array.find((f: any) => f == name)
- if (alarm == null) {
- alarmList.array.push(name)
- }
- }
- if (trueLight == 'yellow') {
- var warn = warningList.array.find((f: any) => f == name)
- if (warn == null) {
- warningList.array.push(name)
- }
- }
- } else {
- alarmList.array = alarmList.array.filter((f: any) => f != name)
- warningList.array = warningList.array.filter((f: any) => f != name)
- }
- }
- /**
- *
- * @param el 传入的元素
- * @param isFirst 判断是不是 单独的三个
- */
- const getBgColor = (el: any) => {
- const obj: any = {
- green: '#6AD28C',
- red: '#FE5656',
- yellow: '#F49F51',
- gray: '#999999'
- }
- if (el.value || el.value === 'true') {
- return obj[el.trueLight]
- } else {
- return obj[el.falseLight]
- }
- }
- /**
- *
- * @param el 传入的元素
- * @param isFirst 判断是不是 单独的三个
- */
- const getMatchBgColor = (el: any) => {
- const obj: any = {
- green: '#6AD28C',
- red: '#FE5656',
- yellow: '#F49F51',
- gray: '#999999'
- }
- // 是否翻转 true 翻转, false 不翻转
- if (el.sValue || el.sValue === 'true') {
- return obj[el.trueLight]
- } else {
- return obj[el.falseLight]
- }
- }
- </script>
- <style lang="less" scoped>
- .content {
- background-color: #c8cfdf;
- height: 100%;
- }
- .left {
- width: 100%;
- border-right: 1px solid #e5e7ee;
- display: flex;
- flex-direction: column;
- &__head {
- width: calc(100% - 20px);
- display: flex;
- flex-direction: row;
- height: 260px;
- padding: 0px 30px;
- align-items: center;
- background: linear-gradient(36deg, #86b0e9, #5f7cd6);
- border-radius: 15px;
- justify-content: space-between;
- .info {
- color: #fff;
- }
- .info__img {
- background-image: url('../../../assets/img/equipment2.png');
- background-size: 100% 100%;
- width: 300px;
- height: 300px;
- }
- .isOnLine {
- filter: grayscale(100%);
- }
- .info1 {
- background-color: rgba(42, 55, 110, 0.4);
- font-weight: bold;
- font-size: 16px;
- width: 87px;
- padding: 5px;
- text-align: center;
- border-radius: 5px;
- }
- .info2 {
- margin: 15px 0;
- font-weight: bold;
- font-size: 24px;
- }
- .info3 {
- margin: 5px 0;
- font-size: 12px;
- span:first-child {
- display: inline-block;
- width: 60px;
- }
- }
- }
- .head__second {
- width: 260px;
- min-width: 260px;
- margin-left: 20px;
- border-radius: 10px;
- .second-info {
- height: 75px;
- display: flex;
- border-radius: 15px;
- background: RGBA(229, 193, 164, 0.5);
- border: 1px solid #c49159;
- align-items: center;
- padding-left: 20px;
- }
- ::v-deep(.second-info) {
- .el-carousel__indicator {
- display: none;
- }
- }
- .second-info:first-child {
- margin-bottom: 20px;
- background: RGBA(229, 163, 169, 0.5);
- border: 1px solid #cd524a;
- }
- .second-name {
- width: 70px;
- height: 45px;
- background: #cd524a;
- border-radius: 10px;
- color: #fff;
- text-align: center;
- line-height: 45px;
- font-size: 12px;
- margin-right: 10px;
- }
- .second-alarm {
- color: #c44b44;
- font-size: 12px;
- width: 120px;
- }
- .second-warning {
- color: #c57d31;
- font-size: 12px;
- width: 120px;
- }
- .bg2 {
- background: #d79146;
- }
- .second-time {
- font-size: 12px;
- margin: 3px;
- text-align: center;
- }
- .color2 {
- color: #c57d31;
- }
- .second-text {
- align-items: center;
- justify-content: center;
- margin: 3px;
- }
- .icon-right {
- display: flex;
- align-items: center;
- .el-icon {
- font-size: 12px;
- transform: scale(0.9);
- }
- }
- .second-text {
- font-size: 12px;
- }
- }
- &__footer {
- display: flex;
- flex-direction: column;
- margin-right: 20px;
- border: 3px solid #e5e7ee;
- border-radius: 15px;
- margin-top: 20px;
- margin-bottom: 20px;
- .footer__info {
- min-height: 170px;
- align-items: center;
- }
- .item {
- flex: 1;
- height: 100%;
- text-align: center;
- display: flex;
- flex-direction: column;
- justify-content: center;
- border-right: 2px solid #e5e7ee;
- &:last-child {
- border: none;
- }
- .text1 {
- font-size: 36px;
- font-weight: bold;
- }
- .weight-color {
- position: absolute;
- width: 20px;
- height: 4px;
- border-radius: 2px;
- position: absolute;
- left: calc(50% + 20px);
- top: 50%;
- transform: translateY(-50%);
- }
- .text2 {
- color: #4a5373;
- margin-top: 8px;
- position: relative;
- }
- }
- .footer__info2 {
- margin-top: 60px;
- margin-bottom: 30px;
- }
- .item2 {
- flex: 1;
- text-align: center;
- .text1 {
- width: 80px;
- height: 7px;
- background: #6ad28c;
- border-radius: 3px;
- margin: 0 auto;
- }
- .text2 {
- color: #4a5373;
- font-size: 14px;
- margin-top: 10px;
- }
- }
- }
- }
- .right {
- width: 490px;
- height: 100%;
- padding-left: 16px;
- &__item {
- height: 40px;
- margin: 0 30px;
- line-height: 40px;
- border-bottom: 1px solid #dcddde;
- margin-bottom: 5px;
- .text {
- color: #4a5373;
- font-size: 14px;
- }
- .text2 {
- display: flex;
- align-items: center;
- justify-content: center;
- div {
- width: 30px;
- height: 5px;
- background: #6ad28c;
- border-radius: 3px;
- margin-left: 10px;
- }
- }
- }
- }
- </style>
|