import { GeoFence, GeoFenceAlert } from '../types/equipment'; /** * 生成测试用的电子围栏数据 */ export function generateTestGeoFences(machineryIds: string[]): GeoFence[] { const now = new Date().toISOString(); return [ // 矩形作业区 { id: 'fence-test-1', name: '1号作业区', type: 'polygon', points: [ { latitude: 36.6512, longitude: 117.1201 }, { latitude: 36.6532, longitude: 117.1201 }, { latitude: 36.6532, longitude: 117.1251 }, { latitude: 36.6512, longitude: 117.1251 }, ], machineryIds: machineryIds.slice(0, 2), alertOnExit: true, alertOnEnter: false, countWorkHours: true, enabled: true, createdAt: now, updatedAt: now, createdBy: '系统管理员', }, // 圆形禁入区 { id: 'fence-test-2', name: '东侧水塘禁入区', type: 'circle', center: { latitude: 36.6500, longitude: 117.1200 }, radius: 200, machineryIds: machineryIds, alertOnExit: false, alertOnEnter: true, countWorkHours: false, enabled: true, createdAt: now, updatedAt: now, createdBy: '系统管理员', }, // 大型多边形作业区 { id: 'fence-test-3', name: '2号地块-西区', type: 'polygon', points: [ { latitude: 36.6480, longitude: 117.1150 }, { latitude: 36.6500, longitude: 117.1150 }, { latitude: 36.6510, longitude: 117.1180 }, { latitude: 36.6500, longitude: 117.1200 }, { latitude: 36.6480, longitude: 117.1190 }, ], machineryIds: machineryIds.slice(0, 1), alertOnExit: true, alertOnEnter: false, countWorkHours: true, enabled: true, createdAt: now, updatedAt: now, createdBy: '系统管理员', }, // 休息区(圆形) { id: 'fence-test-4', name: '农机停放区', type: 'circle', center: { latitude: 36.6550, longitude: 117.1100 }, radius: 100, machineryIds: machineryIds, alertOnExit: false, alertOnEnter: false, countWorkHours: false, enabled: true, createdAt: now, updatedAt: now, createdBy: '系统管理员', }, ]; } /** * 生成测试用的围栏报警数据 */ export function generateTestGeoFenceAlerts( fences: GeoFence[], machineryData: { id: string; name: string }[] ): GeoFenceAlert[] { const alerts: GeoFenceAlert[] = []; // 生成一些历史报警 if (fences.length > 0 && machineryData.length > 0) { const fence1 = fences[0]; const machinery1 = machineryData[0]; alerts.push({ id: 'alert-test-1', fenceId: fence1.id, fenceName: fence1.name, machineryId: machinery1.id, machineryName: machinery1.name, alertType: 'exit', location: { latitude: 36.6510, longitude: 117.1252 }, alertedAt: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(), // 2小时前 acknowledged: false, }); } if (fences.length > 1 && machineryData.length > 1) { const fence2 = fences[1]; const machinery2 = machineryData[1]; alerts.push({ id: 'alert-test-2', fenceId: fence2.id, fenceName: fence2.name, machineryId: machinery2.id, machineryName: machinery2.name, alertType: 'enter', location: { latitude: 36.6501, longitude: 117.1201 }, alertedAt: new Date(Date.now() - 30 * 60 * 1000).toISOString(), // 30分钟前 acknowledged: true, acknowledgedBy: '值班员张三', acknowledgedAt: new Date(Date.now() - 25 * 60 * 1000).toISOString(), }); } return alerts; } /** * 验证围栏坐标是否有效 */ export function validateGeoFence(fence: Partial): { valid: boolean; errors: string[] } { const errors: string[] = []; if (!fence.name || fence.name.trim() === '') { errors.push('围栏名称不能为空'); } if (!fence.type) { errors.push('必须选择围栏类型'); } if (fence.type === 'circle') { if (!fence.center) { errors.push('圆形围栏必须设置中心点'); } else { if (!isValidLatitude(fence.center.latitude)) { errors.push('中心点纬度无效(应在-90到90之间)'); } if (!isValidLongitude(fence.center.longitude)) { errors.push('中心点经度无效(应在-180到180之间)'); } } if (!fence.radius || fence.radius <= 0) { errors.push('圆形围栏半径必须大于0'); } } if (fence.type === 'polygon') { if (!fence.points || fence.points.length < 3) { errors.push('多边形围栏至少需要3个顶点'); } else { fence.points.forEach((point, index) => { if (!isValidLatitude(point.latitude)) { errors.push(`顶点${index + 1}的纬度无效`); } if (!isValidLongitude(point.longitude)) { errors.push(`顶点${index + 1}的经度无效`); } }); } } if (!fence.machineryIds || fence.machineryIds.length === 0) { errors.push('必须至少关联一台农机'); } if (!fence.alertOnEnter && !fence.alertOnExit) { errors.push('建议至少启用一种报警方式(进入或离开)'); } return { valid: errors.length === 0, errors, }; } /** * 验证纬度是否有效 */ function isValidLatitude(lat: number): boolean { return !isNaN(lat) && lat >= -90 && lat <= 90; } /** * 验证经度是否有效 */ function isValidLongitude(lng: number): boolean { return !isNaN(lng) && lng >= -180 && lng <= 180; } /** * 计算两点之间的距离(米) * 使用 Haversine 公式 */ export function calculateDistance( point1: { latitude: number; longitude: number }, point2: { latitude: number; longitude: number } ): number { const R = 6371000; // 地球半径,米 const lat1 = (point1.latitude * Math.PI) / 180; const lat2 = (point2.latitude * Math.PI) / 180; const deltaLat = ((point2.latitude - point1.latitude) * Math.PI) / 180; const deltaLng = ((point2.longitude - point1.longitude) * Math.PI) / 180; const a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(deltaLng / 2) * Math.sin(deltaLng / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; } /** * 判断点是否在圆形围栏内 */ export function isPointInCircle( point: { latitude: number; longitude: number }, center: { latitude: number; longitude: number }, radius: number ): boolean { const distance = calculateDistance(point, center); return distance <= radius; } /** * 判断点是否在多边形围栏内 * 使用射线法(Ray Casting Algorithm) */ export function isPointInPolygon( point: { latitude: number; longitude: number }, polygon: { latitude: number; longitude: number }[] ): boolean { let inside = false; const x = point.longitude; const y = point.latitude; for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { const xi = polygon[i].longitude; const yi = polygon[i].latitude; const xj = polygon[j].longitude; const yj = polygon[j].latitude; const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi; if (intersect) { inside = !inside; } } return inside; }