| | |
| | | const EPSILON = 1; // 容差,用于处理浮点数精度问题 |
| | | const EPSILON = 1e-9; // 容差,用于处理浮点数精度问题 |
| | | |
| | | const nowMs = () => |
| | | typeof performance !== 'undefined' && performance.now ? performance.now() : Date.now(); |
| | |
| | | throw new Error('mapTrackGeometry.js must be loaded before MapCanvas.js'); |
| | | } |
| | | |
| | | // 仅本地测试用 |
| | | const FAKE_MAX_LAYER = 1730000; |
| | | const FAKE_MAX_CRN_LAYER = 4800 |
| | | /** |
| | | * 通用轮询器类 |
| | | * 封装带请求取消、平滑延迟计算、错误退避的轮询逻辑 |
| | |
| | | shelfChunkSize: 2048, |
| | | shelfCullPadding: 160, |
| | | shelfCullRaf: null, |
| | | crnList: [], |
| | | dualCrnList: [], |
| | | rgvList: [], |
| | | locListMap: new Map(), |
| | | locListLoaded: false, |
| | | locListLoading: false, |
| | |
| | | tracksGraphics: null, |
| | | shelvesContainer: null, |
| | | graphicsCrn: null, |
| | | graphicsCrnTrack: null, |
| | | graphicsRgvTrack: null, |
| | | // graphicsCrnTrack: null, |
| | | // graphicsRgvTrack: null, |
| | | graphicsRgv: null, |
| | | shelfTooltip: { |
| | | visible: false, |
| | |
| | | 'machine-pakin': 0x30bffc, |
| | | 'machine-pakout': 0x97b400, |
| | | 'site-run-block': 0xe69138, |
| | | 'site-error': 0xDB2828 |
| | | 'site-error': 0xdb2828 |
| | | }, |
| | | fakeOperationVisible: false |
| | | } |
| | | }; |
| | | }, |
| | | mounted() { |
| | | mounted() { |
| | | this.DEVICE_MAP = { |
| | | crn: { |
| | | createTexture: this.createCrnTexture, |
| | | graphics: this.graphicsCrn, |
| | | emitName: 'crn-click', |
| | | pixiMap: this.pixiCrnMap, |
| | | type: 'crn', |
| | | idName: 'crnId', |
| | | statusInfo: { |
| | | name: 'crnStatus', |
| | | getStatus: this.getCrnStatusColor, |
| | | updateTextureColor: this.updateCrnTextureColor |
| | | } |
| | | }, |
| | | dualcrn: { |
| | | createTexture: this.createCrnTexture, |
| | | graphics: this.graphicsCrn, |
| | | emitName: 'dual-crn-click', |
| | | pixiMap: this.pixiDualCrnMap, |
| | | type: 'dualCrn', |
| | | idName: 'crnId', |
| | | statusInfo: { |
| | | name: 'crnStatus', |
| | | getStatus: this.getCrnStatusColor, |
| | | updateTextureColor: this.updateCrnTextureColor |
| | | } |
| | | }, |
| | | rgv: { |
| | | createTexture: this.createRgvTexture, |
| | | graphics: this.graphicsRgv, |
| | | emitName: 'rgv-click', |
| | | pixiMap: this.pixiRgvMap, |
| | | type: 'rgv', |
| | | idName: 'rgvNo', |
| | | statusInfo: { |
| | | name: 'rgvStatus', |
| | | getStatus: this.getRgvStatusColor, |
| | | updateTextureColor: this.updateRgvTextureColor |
| | | } |
| | | } |
| | | }; |
| | | this.DEVICE_MAP.dualCrn = this.DEVICE_MAP.dualcrn; |
| | | this.currentLev = this.lev || 1; |
| | | this.createMap(); |
| | | this.startContainerResizeObserve(); |
| | |
| | | this.getRgvInfo(); |
| | | }, 1000); |
| | | |
| | | this.startAnnulusDevicePoll(); |
| | | setTimeout(()=>{ |
| | | this.startAnnulusDevicePoll(); |
| | | },1000) |
| | | |
| | | // todo:测试代码 |
| | | setTimeout(() => { |
| | |
| | | * 切换楼层或重载地图前的共用清场(可选:对设备精灵 kill GSAP)。 |
| | | * @param {{ killDeviceGsap?: boolean, skipClearLoopHighlight?: boolean }} options |
| | | */ |
| | | prepareMapSceneReload(options) { |
| | | const opts = options || {}; |
| | | if (!opts.skipClearLoopHighlight) { |
| | | this.clearLoopStationHighlight(); |
| | | } |
| | | clearMap() { |
| | | // const opts = options || {}; |
| | | // if (!opts.skipClearLoopHighlight) { |
| | | // this.clearLoopStationHighlight(); |
| | | // } |
| | | this.hideShelfTooltip(); |
| | | this.hoveredShelfCell = null; |
| | | this.mapRowOffsets = []; |
| | |
| | | clearTimeout(this.adjustLabelTimer); |
| | | this.adjustLabelTimer = null; |
| | | } |
| | | if (opts.killDeviceGsap && window.gsap) { |
| | | [this.pixiStaMap, this.pixiCrnMap, this.pixiDualCrnMap, this.pixiRgvMap].forEach((m) => { |
| | | if (window.gsap) { |
| | | [this.pixiStaMap].forEach((m) => { |
| | | m && |
| | | m.forEach((s) => { |
| | | try { |
| | |
| | | }); |
| | | } |
| | | this.objectsContainer.removeChildren(); |
| | | this.clearShelfChunks(); |
| | | this.pixiStaMap = new Map(); |
| | | this.pixiStageList = []; |
| | | }, |
| | | clearMap2() { |
| | | if (window.gsap) { |
| | | [this.pixiCrnMap, this.pixiDualCrnMap, this.pixiRgvMap].forEach((m) => { |
| | | m && |
| | | m.forEach((s) => { |
| | | try { |
| | | window.gsap.killTweensOf(s); |
| | | } catch (e) {} |
| | | }); |
| | | }); |
| | | } |
| | | this.objectsContainer2.removeChildren(); |
| | | if (this.tracksContainer) { |
| | | this.tracksContainer.removeChildren(); |
| | |
| | | if (this.tracksGraphics) { |
| | | this.tracksGraphics.clear(); |
| | | } |
| | | this.clearShelfChunks(); |
| | | this.crnList = []; |
| | | this.dualCrnList = []; |
| | | this.rgvList = []; |
| | | this.pixiCrnMap.clear(); |
| | | this.pixiDualCrnMap.clear(); |
| | | this.pixiRgvMap.clear(); |
| | | this.pixiStaMap = new Map(); |
| | | this.pixiStageList = []; |
| | | }, |
| | | cycleCapacityPanelStyle() { |
| | | const hud = this.hudPadding || {}; |
| | |
| | | this.pixiApp.view.style.display = 'block'; |
| | | this.resizeToContainer(); |
| | | window.addEventListener('resize', this.scheduleResizeToContainer); |
| | | this.graphicsCrnTrack = this.createTrackTexture(25, 25, 10); |
| | | this.graphicsRgvTrack = this.createTrackTexture(25, 25, 10); |
| | | // this.graphicsCrnTrack = this.createTrackTexture(25, 25, 10); |
| | | // this.graphicsRgvTrack = this.createTrackTexture(25, 25, 10); |
| | | this.objectsContainer = new PIXI.Container(); |
| | | this.objectsContainer2 = new PIXI.Container(); |
| | | this.tracksContainer = new PIXI.ParticleContainer(10000, { |
| | |
| | | data: {} |
| | | }) |
| | | ); |
| | | this.setMap2(); |
| | | }, |
| | | changeFloor(lev) { |
| | | this.currentLev = lev; |
| | | this.clearLoopStationHighlight(); |
| | | this.isSwitchingFloor = true; |
| | | this.prepareMapSceneReload({ killDeviceGsap: false, skipClearLoopHighlight: true }); |
| | | this.getMap(); |
| | | }, |
| | | setMap(res) { |
| | | this.clearMap(); |
| | | this.createMapData(JSON.parse(res.data)); |
| | | }, |
| | | // 轨道在Map2中 |
| | |
| | | method: 'get', |
| | | success: function (res) { |
| | | if (res && res.code === 200 && res.data && res.data.elements) { |
| | | this.clearMap2(); |
| | | this.createMap2Data( |
| | | res.data.elements.filter((item) => |
| | | ['crn', 'dualCrn', 'rgv', 'annulus'].includes(item.type) |
| | |
| | | }); |
| | | }, |
| | | createMapData(map) { |
| | | this.prepareMapSceneReload({ killDeviceGsap: true }); |
| | | this.pixiStageList = [map.length]; |
| | | |
| | | this.setMap2(); |
| | | |
| | | const bayHeightList = this.initHeight(map); |
| | | const bayWidthList = this.initWidth(map); |
| | |
| | | if (val.type == undefined || val.type === 'none') { |
| | | continue; |
| | | } |
| | | // if (this.isTrackType(val)) { |
| | | // this.collectTrackItem(val); |
| | | // continue; |
| | | // } |
| | | if (val.type === 'shelf') { |
| | | continue; |
| | | } |
| | | if (['crn', 'dualCrn', 'rgv', 'annulus'].includes(val.type)) { |
| | | continue; |
| | | } |
| | | // 收集crnList等 |
| | | let sprite = this.getSprite(val, (e) => {}); |
| | | if (sprite == null) { |
| | | continue; |
| | |
| | | // 每个设备独立创建纹理,不要缓存复用,否则所有设备尺寸会相同 |
| | | const graphics = deviceTypeInfo.createTexture(along, across); |
| | | let sprite = new PIXI.Sprite(graphics); |
| | | // anchor 居中后子坐标原点在纹理中心,编号须用 (0,0),见 positionSpriteLabelToTextureCenter |
| | | sprite.anchor.set(0.5); |
| | | const deviceNo = device.deviceNo || device[deviceTypeInfo.idName]; |
| | | const style = new PIXI.TextStyle({ |
| | | fontFamily: 'Arial', |
| | | fontSize: 10, |
| | | fontSize: 12, |
| | | fill: '#000000', |
| | | stroke: '#ffffff', |
| | | strokeThickness: 1, |
| | |
| | | const text = new PIXI.Text(txt, style); |
| | | text.anchor.set(0.5); |
| | | sprite.addChild(text); |
| | | text.position.set(sprite.width / 2, sprite.height / 2); |
| | | sprite.textObj = text; |
| | | this.positionSpriteLabelToTextureCenter(sprite); |
| | | |
| | | // 这里item已经是中心点了,直接用就行 |
| | | device.width = sprite.width; |
| | |
| | | sprite.currentAngle = mappingInfo.angle; |
| | | // sprite.vector = mappingInfo.vector |
| | | // sprite.time = Date.now() |
| | | sprite.anchor.set(0.5); |
| | | sprite.rotation = G.getRotate(mappingInfo, mappingInfo.path) || sprite.rotation; |
| | | sprite.x = mappingInfo.x; |
| | | sprite.y = mappingInfo.y; |
| | |
| | | return; |
| | | } |
| | | if (workNo != null && workNo > 0) { |
| | | sta.textObj.text = id + '(' + workNo + ')'; |
| | | sta.textObj.text = String(id) + '\n(' + workNo + ')'; |
| | | } else { |
| | | sta.textObj.text = String(id); |
| | | } |
| | |
| | | } |
| | | return (value * 100).toFixed(1) + '%'; |
| | | }, |
| | | /******************setDeviceInfo用的函数:******************/ |
| | | //设备编号:画布坐标系固定字号,随地图缩放与设备图标同比例变化(避免缩小视图时相对设备变大) |
| | | applyEditorLikeTrackDeviceTextStyle(textObj) { |
| | | if (!textObj || !textObj.style) { |
| | | return; |
| | | } |
| | | textObj.style.fontSize = 10; |
| | | textObj.style.strokeThickness = 1; |
| | | }, |
| | | parseHexColor(color) { |
| | | if (typeof color !== 'string' || color.charAt(0) !== '#') { |
| | | return null; |
| | | } |
| | | let hex = color.slice(1); |
| | | if (hex.length === 3) { |
| | | hex = hex.replace(/(.)/g, '$1$1'); |
| | | } |
| | | if (!/^[0-9a-fA-F]{6}$/.test(hex)) { |
| | | return null; |
| | | } |
| | | return parseInt(hex, 16); |
| | | }, |
| | | /******************setDeviceInfo使用的函数:******************/ |
| | | getAnnulusAwarePoint(trackInfo, x, y, path) { |
| | | return trackInfo.type === 'annulus' && G.snapToAnnulusOuterPath |
| | | return trackInfo.type === 'annulus' |
| | | ? G.snapToAnnulusOuterPath(x, y, path) |
| | | : { x, y }; |
| | | }, |
| | |
| | | const vx = path0.x - x0; |
| | | const vy = path0.y - y0; |
| | | const segLen = Math.sqrt(vx * vx + vy * vy); |
| | | if (!(segLen > 1e-6)) { |
| | | if (!(segLen > EPSILON)) { |
| | | return null; |
| | | } |
| | | const clampT = (px, py) => { |
| | |
| | | } else { |
| | | sprite.textObj.text = String(id); |
| | | } |
| | | this.applyEditorLikeTrackDeviceTextStyle(sprite.textObj); |
| | | const statusColor = this.parseHexColor(device.statusColor); |
| | | const statusColor = device.statusColor; |
| | | if (statusColor != null) { |
| | | deviceTypeInfo.statusInfo.updateTextureColor(sprite, statusColor); |
| | | } |
| | |
| | | mappingInfo.path |
| | | ); |
| | | sprite._barcodeAnchor = this.createBarcodeAnchorState( |
| | | sprite.id, |
| | | sprite.trackInfo.id, |
| | | minBarcode, |
| | | maxBarcode, |
| | | totalSegmentCount, |
| | |
| | | ); |
| | | if (remainAlong != null) { |
| | | let stepMag = Math.min(stepCap, Math.abs(remainAlong), restDistance); |
| | | if (stepMag < 1e-6 && restDistance > EPSILON) { |
| | | if (stepMag < EPSILON && restDistance > EPSILON) { |
| | | const x0 = path.startX; |
| | | const y0 = path.startY; |
| | | const vx = path.x - x0; |
| | | const vy = path.y - y0; |
| | | const sl = Math.sqrt(vx * vx + vy * vy); |
| | | if (sl > 1e-6) { |
| | | if (sl > EPSILON) { |
| | | const ux = vx / sl; |
| | | const uy = vy / sl; |
| | | const wdx = sprite.mappingInfo.x - sprite.x; |
| | |
| | | let anchor = sprite._barcodeAnchor; |
| | | const needResetAnchor = |
| | | !anchor || |
| | | anchor.trackId !== sprite.id || |
| | | anchor.trackId !== sprite.trackInfo.id || |
| | | anchor.minBarcode !== minBarcode || |
| | | anchor.maxBarcode !== maxBarcode || |
| | | anchor.totalSegmentCount !== totalSegmentCount; |
| | |
| | | ); |
| | | const anchorBarcode = oldSprite.barcode != null ? oldSprite.barcode : device.rgvPos; |
| | | anchor = this.createBarcodeAnchorState( |
| | | sprite.id, |
| | | sprite.trackInfo.id, |
| | | minBarcode, |
| | | maxBarcode, |
| | | totalSegmentCount, |
| | |
| | | deltaDistance, |
| | | anchor.angle |
| | | ); |
| | | let curveDistance = G.calcDistance(oldSprite.mappingInfo || oldSprite, finalPosition); |
| | | let curveDistance = G.calcDistance(sprite, finalPosition); |
| | | if (!isFinite(curveDistance)) { |
| | | curveDistance = deltaDistance; |
| | | } |
| | |
| | | ? sprite.maV * (1 - alpha) + v * alpha |
| | | : v; |
| | | } |
| | | if(device.index === 18) { |
| | | console.log("device",device,curveDistance,oldSprite.barcode, device.rgvPos,sprite.id) |
| | | } |
| | | if (curveDistance < EPSILON) { |
| | | this.finishDeviceMotion(sprite); |
| | | return; |
| | |
| | | deviceAdapter(type, res, trackType) { |
| | | const devices = this.parseBarcodeDevicesResponse(res); |
| | | const deviceTypeInfo = this.DEVICE_MAP[type]; |
| | | // 环穿接口 |
| | | if (trackType === 'annulus') { |
| | | return devices.map((device) => { |
| | | const index = +device.index; |
| | |
| | | return {}; |
| | | } |
| | | const trackInfoParse = G.safeParseJson(sprite.trackInfo.value); |
| | | const statusColorStr = device.statusColor.split('#')[1]; |
| | | const statusColor = parseInt(statusColorStr, 16); |
| | | return { |
| | | index, |
| | | statusColor: device.statusColor, |
| | | statusColor, |
| | | sprite, |
| | | minBarcode: trackInfoParse.barCodeStart, |
| | | maxBarcode: trackInfoParse.barCodeEnd, |
| | |
| | | }; |
| | | }); |
| | | } |
| | | // 原版接口 |
| | | return devices.map((device) => { |
| | | const index = +device[deviceTypeInfo.idName]; |
| | | const sprite = deviceTypeInfo.pixiMap.get(index); |
| | | if (!sprite) { |
| | | return {}; |
| | | } |
| | | const trackInfoParse = G.safeParseJson(sprite.trackInfo.value); |
| | | const minBarcode = trackInfoParse.barCodeStart; |
| | | const maxBarcode = trackInfoParse.barCodeEnd; |
| | | const statusInfo = deviceTypeInfo.statusInfo; |
| | | const statusName = device[statusInfo.statusName]; |
| | | const statusColor = statusInfo[statusInfo.getStatus](statusName); |
| | | const statusColor = statusInfo.getStatus(device[statusInfo.name]); |
| | | return { |
| | | index, |
| | | statusColor, |
| | | sprite, |
| | | minBarcode, |
| | | maxBarcode, |
| | | rgvPos: device.rgvPos, |
| | | rgvPos: device.laserValue * FAKE_MAX_LAYER / FAKE_MAX_CRN_LAYER, |
| | | taskNo: device.taskNo |
| | | }; |
| | | }); |
| | |
| | | } |
| | | this.scheduleAdjustLabels(); |
| | | }, |
| | | /******************setDeviceInfo用的函数结束******************/ |
| | | /******************setDeviceInfo相关函数结束******************/ |
| | | webSocketOnOpen(e) { |
| | | if (this.wsReconnectTimer) { |
| | | clearTimeout(this.wsReconnectTimer); |
| | |
| | | result.url === '/console/latest/data/station' || |
| | | result.url === '/console/latest/data/site' |
| | | ) { |
| | | this.setSiteInfo(JSON.parse(result.data)); |
| | | // todo |
| | | // this.setSiteInfo(JSON.parse(result.data)); |
| | | } else if (result.url === '/console/latest/data/crn') { |
| | | // this.setDeviceInfo('crn', JSON.parse(result.data)); |
| | | this.setDeviceInfoByBarcode('crn', JSON.parse(result.data), 'crn'); |
| | | } else if (result.url === '/console/latest/data/dualcrn') { |
| | | // this.setDeviceInfo('dualcrn', JSON.parse(result.data)); |
| | | this.setDeviceInfoByBarcode('dualCrn', JSON.parse(result.data), 'dualCrn'); |
| | | } else if (result.url === '/console/latest/data/rgv') { |
| | | // this.setDeviceInfo('rgv', JSON.parse(result.data)); |
| | | // this.setDeviceInfoByBarcode('rgv', JSON.parse(result.data), 'rgv'); |
| | | } else if (result.url === '/console/latest/data/station/cycle/capacity') { |
| | | this.setCycleCapacityInfo(JSON.parse(result.data)); |
| | | } else if (typeof result.url === 'string' && result.url.indexOf('/basMap/lev/') === 0) { |
| | |
| | | } |
| | | return new PIXI.Sprite(texture); |
| | | }, |
| | | createTrackSprite(width, height, mask) { |
| | | const trackMask = mask != null ? mask : 10; |
| | | let idx = width + '-' + height + '-' + trackMask; |
| | | let texture = this.pixiTrackMap.get(idx); |
| | | if (texture == undefined) { |
| | | texture = this.createTrackTexture(width, height, trackMask); |
| | | this.pixiTrackMap.set(idx, texture); |
| | | } |
| | | return new PIXI.Sprite(texture); |
| | | }, |
| | | getContainer(type, width, height) { |
| | | let graphics = new PIXI.Graphics(); |
| | | let drawBorder = true; |
| | |
| | | } |
| | | graphics.endFill(); |
| | | return graphics; |
| | | }, |
| | | createTrackTexture(width, height, mask) { |
| | | const TRACK_N = 1; |
| | | const TRACK_E = 2; |
| | | const TRACK_S = 4; |
| | | const TRACK_W = 8; |
| | | const trackMask = mask != null ? mask : TRACK_E | TRACK_W; |
| | | const g = new PIXI.Graphics(); |
| | | const size = Math.max(1, Math.min(width, height)); |
| | | const rail = Math.max(2, Math.round(size * 0.12)); |
| | | const gap = Math.max(4, Math.round(size * 0.38)); |
| | | const midX = Math.round(width / 2); |
| | | const midY = Math.round(height / 2); |
| | | const y1 = midY - Math.round(gap / 2); |
| | | const y2 = midY + Math.round(gap / 2); |
| | | const x1 = midX - Math.round(gap / 2); |
| | | const x2 = midX + Math.round(gap / 2); |
| | | |
| | | const hasN = (trackMask & TRACK_N) !== 0; |
| | | const hasE = (trackMask & TRACK_E) !== 0; |
| | | const hasS = (trackMask & TRACK_S) !== 0; |
| | | const hasW = (trackMask & TRACK_W) !== 0; |
| | | |
| | | const hStart = hasW ? 0 : midX; |
| | | const hEnd = hasE ? width : midX; |
| | | const vStart = hasN ? 0 : midY; |
| | | const vEnd = hasS ? height : midY; |
| | | |
| | | const railColor = 0x555555; |
| | | const drawLine = (x1p, y1p, x2p, y2p, w, color) => { |
| | | g.lineStyle(w, color, 1); |
| | | g.moveTo(x1p, y1p); |
| | | g.lineTo(x2p, y2p); |
| | | }; |
| | | |
| | | const hasH = hasW || hasE; |
| | | const hasV = hasN || hasS; |
| | | const isCorner = hasH && hasV && !(hasW && hasE) && !(hasN && hasS); |
| | | if (hasH && !isCorner) { |
| | | const w = Math.max(1, hEnd - hStart); |
| | | g.beginFill(railColor); |
| | | g.drawRect(hStart, midY - Math.round(rail / 2), w, rail); |
| | | g.endFill(); |
| | | } |
| | | if (hasV && !isCorner) { |
| | | const h = Math.max(1, vEnd - vStart); |
| | | g.beginFill(railColor); |
| | | g.drawRect(midX - Math.round(rail / 2), vStart, rail, h); |
| | | g.endFill(); |
| | | } |
| | | if (isCorner) { |
| | | const cw = hasE; |
| | | const ch = hasS; |
| | | const cx = cw ? width - 1 : 0; |
| | | const cy = ch ? height - 1 : 0; |
| | | const angStart = cw && ch ? Math.PI : cw ? Math.PI / 2 : ch ? -Math.PI / 2 : 0; |
| | | const angEnd = cw && ch ? Math.PI * 1.5 : cw ? Math.PI : ch ? 0 : Math.PI / 2; |
| | | const rX = Math.abs(cx - midX); |
| | | const rY = Math.abs(cy - midY); |
| | | const rMid = Math.min(rX, rY); |
| | | g.lineStyle(rail, railColor, 1); |
| | | g.arc(cx, cy, rMid, angStart, angEnd); |
| | | g.lineStyle(0, 0, 0); |
| | | } |
| | | |
| | | // no sleepers; keep a single continuous line |
| | | const rt = PIXI.RenderTexture.create({ width: width, height: height }); |
| | | this.pixiApp.renderer.render(g, rt); |
| | | return rt; |
| | | }, |
| | | createCrnTexture(width, height) { |
| | | const g = new PIXI.Graphics(); |
| | |
| | | const fill = this.getContrastColor(color); |
| | | textObj.style.fill = fill; |
| | | textObj.style.stroke = fill === '#000000' ? '#ffffff' : '#000000'; |
| | | this.applyEditorLikeTrackDeviceTextStyle(textObj); |
| | | textObj.style.strokeThickness = 1; |
| | | if (repositionText) { |
| | | textObj.position.set(sprite.width / 2, sprite.height / 2); |
| | | this.positionSpriteLabelToTextureCenter(sprite); |
| | | } |
| | | }, |
| | | updateRgvTextureColor(sprite, color) { |
| | |
| | | return colorMap['site-unauto'] != null ? colorMap['site-unauto'] : 0xb8b8b8; |
| | | }, |
| | | resolveStationStatus(item) { |
| | | if (item && item.error > 0) { return 'site-error'; } |
| | | if (item && item.error > 0) { |
| | | return 'site-error'; |
| | | } |
| | | const status = item && (item.siteStatus != null ? item.siteStatus : item.stationStatus); |
| | | const taskNo = this.parseStationTaskNo( |
| | | item && (item.workNo != null ? item.workNo : item.taskNo) |
| | |
| | | fontSize: 10, |
| | | fill: '#000000', |
| | | stroke: '#ffffff', |
| | | strokeThickness: 1 |
| | | strokeThickness: 1, |
| | | align: 'center' |
| | | }); |
| | | const text = new PIXI.Text(String(siteId), style); |
| | | text.anchor.set(0.5); |
| | |
| | | this.$emit('station-click', id); |
| | | } |
| | | }); |
| | | } else if (item.type == 'crn') { |
| | | sprite = this.createTrackSprite(item.width, item.height, item.trackMask); |
| | | sprite._kind = 'crn-track'; |
| | | this.crnList.push(item); |
| | | } else if (item.type == 'dualCrn') { |
| | | sprite = this.createTrackSprite(item.width, item.height, item.trackMask); |
| | | sprite._kind = 'crn-track'; |
| | | this.dualCrnList.push(item); |
| | | } else if (item.type == 'rgv') { |
| | | sprite = this.createTrackSprite(item.width, item.height, item.trackMask); |
| | | sprite._kind = 'rgv-track'; |
| | | this.rgvList.push(item); |
| | | } else { |
| | | return null; |
| | | } |
| | | sprite.position.set(item.posX, item.posY); |
| | | return sprite; |
| | | }, |
| | | collectTrackItem(item) { |
| | | const value = item.value; |
| | | if (item.type === 'crn') { |
| | | if (this.getDeviceNo(value) > 0) { |
| | | this.crnList.push(item); |
| | | } |
| | | } else if (item.type === 'dualCrn') { |
| | | if (this.getDeviceNo(value) > 0) { |
| | | this.dualCrnList.push(item); |
| | | } |
| | | } else if (item.type === 'rgv') { |
| | | if (this.getDeviceNo(value) > 0) { |
| | | this.rgvList.push(item); |
| | | } |
| | | } |
| | | }, |
| | | isTrackType(cell) { |
| | | return ( |
| | |
| | | } |
| | | return null; |
| | | }, |
| | | // getTrackMask(map, rowIndex, colIndex) { |
| | | // const TRACK_N = 1; |
| | | // const TRACK_E = 2; |
| | | // const TRACK_S = 4; |
| | | // const TRACK_W = 8; |
| | | // const baseRow = map[rowIndex]; |
| | | // if (!baseRow) { |
| | | // return 0; |
| | | // } |
| | | // const base = baseRow[colIndex]; |
| | | // if (!this.isTrackType(base)) { |
| | | // return 0; |
| | | // } |
| | | // const rowSpan = base.rowSpan || 1; |
| | | // const colSpan = base.colSpan || 1; |
| | | // let mask = 0; |
| | | // const n = this.resolveMergedCell(map, rowIndex - 1, colIndex); |
| | | // const s = this.resolveMergedCell(map, rowIndex + rowSpan, colIndex); |
| | | // const w = this.resolveMergedCell(map, rowIndex, colIndex - 1); |
| | | // const e = this.resolveMergedCell(map, rowIndex, colIndex + colSpan); |
| | | // if (n && n !== base && this.isTrackType(n)) { |
| | | // mask |= TRACK_N; |
| | | // } |
| | | // if (e && e !== base && this.isTrackType(e)) { |
| | | // mask |= TRACK_E; |
| | | // } |
| | | // if (s && s !== base && this.isTrackType(s)) { |
| | | // mask |= TRACK_S; |
| | | // } |
| | | // if (w && w !== base && this.isTrackType(w)) { |
| | | // mask |= TRACK_W; |
| | | // } |
| | | // if (mask === 0) { |
| | | // mask = TRACK_E | TRACK_W; |
| | | // } |
| | | // return mask; |
| | | // }, |
| | | |
| | | drawTracks(map) { |
| | | if (!this.tracksGraphics) { |
| | | return; |
| | |
| | | } |
| | | }, |
| | | getShelfArrangeInfo(item) { |
| | | return item.value; |
| | | if (!item.value) { |
| | | return ''; |
| | | } |
| | | const list = item.value.split('-'); |
| | | return `${list[1]}-${list[0]}`; |
| | | // const parts = []; |
| | | // const matchKey = this.getShelfMatchKey(item); |
| | | // if (matchKey != null) { |
| | |
| | | zIndex: 10 |
| | | }; |
| | | }, |
| | | /** Pixi v5:Sprite.anchor 为 (0.5,0.5) 时局部原点在纹理中心,文字用 (0,0);站点等默认 anchor (0,0) 仍用宽高一半。 */ |
| | | positionSpriteLabelToTextureCenter(sprite) { |
| | | const textObj = sprite && sprite.textObj; |
| | | if (!textObj) { |
| | | return; |
| | | } |
| | | const ax = sprite.anchor != null ? sprite.anchor.x : 0; |
| | | const ay = sprite.anchor != null ? sprite.anchor.y : 0; |
| | | const originAtTextureCenter = |
| | | Math.abs(ax - 0.5) < 0.001 && Math.abs(ay - 0.5) < 0.001; |
| | | if (originAtTextureCenter) { |
| | | textObj.position.set(0, 0); |
| | | } else { |
| | | textObj.position.set(sprite.width / 2, sprite.height / 2); |
| | | } |
| | | }, |
| | | adjustLabelScale() { |
| | | const s = this.pixiApp && this.pixiApp.stage ? Math.abs(this.pixiApp.stage.scale.x || 1) : 1; |
| | | const minPx = 14; |
| | | const viewport = this.getViewportSize(); |
| | | const vw = viewport.width; |
| | | const vh = viewport.height; |
| | | const margin = 50; |
| | | const mirrorSign = this.mapMirrorX ? -1 : 1; |
| | | const inverseRotation = -(((this.mapRotation % 360) * Math.PI) / 180); |
| | | const inverseRotation = -((this.mapRotation % 360) * Math.PI) / 180; |
| | | const tmpPoint = new PIXI.Point(); |
| | | this.pixiStaMap && |
| | | this.pixiStaMap.forEach((sprite) => { |
| | | const textObj = sprite && sprite.textObj; |
| | | if (!textObj) { |
| | | return; |
| | | } |
| | | const base = textObj.style && textObj.style.fontSize ? textObj.style.fontSize : 10; |
| | | let scale = minPx / (base * s); |
| | | if (!isFinite(scale)) { |
| | | scale = 1; |
| | | } |
| | | scale = Math.max(0.8, Math.min(scale, 3)); |
| | | textObj.scale.set(mirrorSign, 1); |
| | | textObj.rotation = inverseRotation; |
| | | textObj.position.set(sprite.width / 2, sprite.height / 2); |
| | | sprite.getGlobalPosition(tmpPoint); |
| | | const on = |
| | | tmpPoint.x >= -margin && |
| | | tmpPoint.y >= -margin && |
| | | tmpPoint.x <= vw + margin && |
| | | tmpPoint.y <= vh + margin; |
| | | textObj.visible = s >= 0.25 && on; |
| | | }); |
| | | [this.pixiCrnMap, this.pixiDualCrnMap, this.pixiRgvMap].forEach((deviceMap) => { |
| | | deviceMap && |
| | | deviceMap.forEach((sprite) => { |
| | | // 标签随地图缩放(与设备/站点几何一致);仅校正水平镜像与地图旋转,不再按 stage 缩放做反向补偿。 |
| | | const apply = (map) => { |
| | | map && |
| | | map.forEach((sprite) => { |
| | | const textObj = sprite && sprite.textObj; |
| | | if (!textObj) { |
| | | return; |
| | | } |
| | | this.applyEditorLikeTrackDeviceTextStyle(textObj); |
| | | textObj.anchor.set(0.5); |
| | | textObj.rotation = 0; |
| | | textObj.scale.set(mirrorSign, 1); |
| | | textObj.position.set(sprite.width / 2, sprite.height / 2); |
| | | textObj.rotation = inverseRotation; |
| | | this.positionSpriteLabelToTextureCenter(sprite); |
| | | sprite.getGlobalPosition(tmpPoint); |
| | | const on = |
| | | tmpPoint.x >= -margin && |
| | |
| | | tmpPoint.y <= vh + margin; |
| | | textObj.visible = s >= 0.25 && on; |
| | | }); |
| | | }); |
| | | }; |
| | | apply(this.pixiStaMap); |
| | | apply(this.pixiCrnMap); |
| | | apply(this.pixiDualCrnMap); |
| | | apply(this.pixiRgvMap); |
| | | }, |
| | | rotateMap() { |
| | | this.mapRotation = (this.mapRotation + 90) % 360; |
| | |
| | | window.open(url, '_blank'); |
| | | }, |
| | | loadFakeProcessStatus() { |
| | | if (typeof window === 'undefined' || typeof $ === 'undefined' || typeof baseUrl === 'undefined') { |
| | | if ( |
| | | typeof window === 'undefined' || |
| | | typeof $ === 'undefined' || |
| | | typeof baseUrl === 'undefined' |
| | | ) { |
| | | this.fakeOperationVisible = false; |
| | | return; |
| | | } |
| | |
| | | }); |
| | | }, |
| | | openFakeOperationConfigPage() { |
| | | if (typeof window === 'undefined' || !this.fakeOperationVisible) { return; } |
| | | const url = (typeof baseUrl !== 'undefined' ? baseUrl : '') + '/views/watch/fakeOperationConfig.html'; |
| | | if (typeof window === 'undefined' || !this.fakeOperationVisible) { |
| | | return; |
| | | } |
| | | const url = |
| | | (typeof baseUrl !== 'undefined' ? baseUrl : '') + '/views/watch/fakeOperationConfig.html'; |
| | | const layerInstance = (window.top && window.top.layer) || window.layer; |
| | | if (layerInstance && typeof layerInstance.open === 'function') { |
| | | layerInstance.open({ |
| | |
| | | 'machine-pakin': 0x30bffc, |
| | | 'machine-pakout': 0x97b400, |
| | | 'site-run-block': 0xe69138, |
| | | 'site-error': 0xDB2828 |
| | | 'site-error': 0xdb2828 |
| | | }; |
| | | }, |
| | | parseColorConfigValue(value, fallback) { |
| | |
| | | return { x, y, angle, path: minPath }; |
| | | }, |
| | | distanceBasedEasingSigmoid(remaining, threshold = 1, steepness = 10, maxSpeedChange = 0.3) { |
| | | // 此乃缓动函数,使用Sigmoid函数作为核心:f(x) = 1 / (1 + e^(-k*(x - threshold))) |
| | | // 缓动函数,使用Sigmoid函数作为核心:f(x) = 1 / (1 + e^(-k*(x - threshold))) |
| | | // remaining: 输入值 |
| | | // threshold: 函数中心点,输出为1 |
| | | // steepness: 曲线陡峭度,控制过渡区的宽度 |
| | |
| | | this.setDeviceInfoByBarcode('rgv', json); |
| | | }, |
| | | //todo: 测试代码 |
| | | fakeSetSiteInfo(staSiteList) { |
| | | const staItem = { |
| | | autoing:true, |
| | | barcode:"", |
| | | emptyMk:false, |
| | | enableIn:false, |
| | | error:0, |
| | | fullPlt:false, |
| | | inBarcodeError:false, |
| | | inEnable:true,ioMode:2,loading:false,outEnable:true,palletHeight:0,runBlock:false,stationId:1102,stationStatus:"site-auto",taskNo:0} |
| | | const list = staSiteList.map(item => { |
| | | return { |
| | | ...staItem, |
| | | ...item |
| | | } |
| | | }) |
| | | this.setSiteInfo(list); |
| | | }, |
| | | async fakeMove(newList = [0, 10, 20, 30, 40, 50], newRaw = {}) { |
| | | const finalList = newList; |
| | | const rawByBarCode = { |
| | |
| | | modeColor: '#4169E1', |
| | | statusColor: '#27AE60', |
| | | rgvPos: 0, |
| | | rgvPosMax: ['el_1775520471475', 0, 100] |
| | | rgvPosMax: ['el_1775520471475', 0, FAKE_MAX_LAYER] |
| | | }; |
| | | const createTimeout = () => { |
| | | const p1 = new Promise((res, rej) => { |
| | |
| | | for await (const p of finalList) { |
| | | await createTimeout(); |
| | | this.setDeviceInfoByBarcode(newRaw.type || 'rgv', [ |
| | | { ...rawByBarCode, ...newRaw, rgvPos: p * 17370 } |
| | | { ...rawByBarCode, ...newRaw, rgvPos: (p * FAKE_MAX_LAYER) / 100 } |
| | | ]); |
| | | } |
| | | }, |
| | |
| | | fill: '#ffffff', |
| | | align: 'center' |
| | | }); |
| | | const label = new PIXI.Text('模拟运动', textStyle); |
| | | const label = new PIXI.Text('模拟运动(仅测试用)', textStyle); |
| | | label.anchor.set(0.5); |
| | | label.position.set(bw / 2, bh / 2); |
| | | const btn = new PIXI.Container(); |
| | |
| | | }, 1000); |
| | | |
| | | // 设备18 |
| | | for (let i = 0; i < 7; i++) { |
| | | list2.push(i * 7); |
| | | for (let i = 0; i <= 8; i++) { |
| | | list2.push(i * 2); |
| | | } |
| | | list2.push(47.8); |
| | | const p2 = this.fakeMove(list2, { index: 18 }); |
| | | p2.then(async () => { |
| | | const firstPhasePos = 17 |
| | | list2.push(firstPhasePos); |
| | | Promise.resolve().then( async ()=>{ |
| | | await this.fakeMove(list2, { index: 18 }); |
| | | await sleep(1000); |
| | | await this.fakeMove([47.8], { index: 18, statusColor: '#a5d6f7' }); |
| | | await this.fakeSetSiteInfo([{stationId:1211,stationStatus:"site-auto-run-id"}]); |
| | | await sleep(1000); |
| | | await this.fakeMove([52.8, 57.8, 62.8, 63.5], { index: 18, statusColor: '#a5d6f7' }); |
| | | await this.fakeSetSiteInfo([{stationId:1209,stationStatus:"site-auto-run-id"},{stationId:1211,stationStatus:"site-auto"}]); |
| | | await sleep(1000); |
| | | await this.fakeMove([63.5], { index: 18, statusColor: '#245a9a' }); |
| | | }); |
| | | await this.fakeSetSiteInfo([{stationId:1208,stationStatus:"site-auto-run-id"},{stationId:1209,stationStatus:"site-auto"}]); |
| | | await sleep(1000); |
| | | await this.fakeSetSiteInfo([{stationId:1208,stationStatus:"site-auto"}]); |
| | | await this.fakeMove([firstPhasePos], { index: 18 , statusColor: '#a5d6f7' }); |
| | | const list2_2 = [] |
| | | for (let i = 1; i <= 8; i++) { |
| | | list2_2.push(firstPhasePos + i * 5.5); |
| | | } |
| | | list2_2.push(62.7) |
| | | await this.fakeMove(list2_2, { index: 18 , statusColor: '#a5d6f7' }); |
| | | await sleep(1000); |
| | | await this.fakeMove([list2_2.at(-1)], { index: 18 , statusColor: '#245a9a' }); |
| | | await this.fakeSetSiteInfo([{stationId:1107,stationStatus:"site-auto-run-id"}]); |
| | | await sleep(1000); |
| | | await this.fakeSetSiteInfo([{stationId:1107,stationStatus:"site-auto"}]); |
| | | await sleep(1000); |
| | | const list_crn = [] |
| | | for (let i = 0; i <= 7; i++) { |
| | | list_crn.push(i * 10); |
| | | } |
| | | await this.fakeMove(list_crn, { index: 15, statusColor: '#a5d6f7', type: 'crn' }); |
| | | await this.fakeMove([list_crn.at(-1)], { index: 15, statusColor: '#245a9a', type: 'crn' }); |
| | | }) |
| | | // await this.fakeMove([47.8], { index: 18, statusColor: '#a5d6f7' }); |
| | | // await sleep(1000); |
| | | // await this.fakeMove([52.8, 57.8, 62.8, 63.5], { index: 18, statusColor: '#a5d6f7' }); |
| | | // await sleep(1000); |
| | | // await this.fakeMove([63.5], { index: 18, statusColor: '#245a9a' }); |
| | | |
| | | // 设备16 |
| | | let list3 = []; |