管灌系统农户端微信小程序(嘉峪关应用)
pages/stationMonitor/stationMonitor.js
@@ -1,5 +1,7 @@
// pages/stationMonitor/stationMonitor.js
const { get } = require('../../api/request.js');
const {
  get
} = require('../../api/request.js');
Page({
@@ -9,15 +11,120 @@
  data: {
    activeTab: 'weather', // 默认选中气象站
    cameraList: [],
    isLoading: false
    isLoading: false,
    // 气象站相关数据
    weatherStationList: [],
    selectedWeatherStationIndex: 0,
    currentWeatherStation: null,
    // 水肥机相关数据
    fertilizerStationList: [],
    selectedFertilizerStationIndex: 0,
    currentFertilizerStation: null,
    // 土壤墒情站相关数据
    soilStationList: [],
    selectedSoilStationIndex: 0,
    currentSoilStation: null,
    //摄像头相关
    accessToken: 'at.4l27eilo2x0euquw4yrhjxnz9kvr294l-2dp10mcwig-1nnzr8p-7wp71d2bk',
    hslUrl: '',
    // 设备信息
    deviceInfo: null,
    isRealDevice: false,
    deviceSpecificConfig: {
      videoHeight: 400,
      buttonHeight: 72,
      fontSize: 26
    }
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {
    // 检测设备类型和屏幕信息
    this.detectDeviceInfo();
    // 页面加载时获取摄像头信息
    this.getCameraList();
    // 获取所有设备信息(气象站、土壤墒情站、水肥机)
    this.getAllDeviceInfo();
  },
  /**
   * 检测设备信息
   */
  detectDeviceInfo() {
    try {
      const systemInfo = wx.getSystemInfoSync();
      console.log('设备信息:', systemInfo);
      // 检测是否为真机
      const isRealDevice = systemInfo.platform === 'android' || systemInfo.platform === 'ios';
      // 检测屏幕尺寸
      const screenWidth = systemInfo.screenWidth;
      const screenHeight = systemInfo.screenHeight;
      const pixelRatio = systemInfo.pixelRatio;
      // 检测微信版本
      const version = systemInfo.version;
      this.setData({
        deviceInfo: {
          platform: systemInfo.platform,
          isRealDevice: isRealDevice,
          screenWidth: screenWidth,
          screenHeight: screenHeight,
          pixelRatio: pixelRatio,
          version: version,
          model: systemInfo.model,
          system: systemInfo.system
        }
      });
      console.log('设备检测结果:', {
        isRealDevice,
        screenWidth,
        screenHeight,
        pixelRatio,
        version
      });
      // 根据设备信息调整布局
      this.adjustLayoutForDevice();
    } catch (error) {
      console.error('获取设备信息失败:', error);
    }
  },
  /**
   * 根据设备信息调整布局
   */
  adjustLayoutForDevice() {
    const { deviceInfo } = this.data;
    if (!deviceInfo) return;
    // 真机特殊处理
    if (deviceInfo.isRealDevice) {
      console.log('检测到真机,应用特殊优化');
      // 真机上可能需要调整一些参数
      this.setData({
        isRealDevice: true,
        // 可以根据设备信息调整其他参数
        deviceSpecificConfig: {
          videoHeight: deviceInfo.screenHeight < 700 ? 320 : 400,
          buttonHeight: deviceInfo.screenHeight < 700 ? 64 : 72,
          fontSize: deviceInfo.pixelRatio > 2 ? 24 : 26
        }
      });
    } else {
      console.log('检测到模拟器');
      this.setData({
        isRealDevice: false
      });
    }
  },
  /**
@@ -31,7 +138,28 @@
   * 生命周期函数--监听页面显示
   */
  onShow() {
    console.log('=== 页面显示 ===');
    console.log('当前页面数据:', {
      activeTab: this.data.activeTab,
      cameraList: this.data.cameraList,
      deviceInfo: this.data.deviceInfo,
      deviceSpecificConfig: this.data.deviceSpecificConfig
    });
    // 检查ezplayer组件的状态
    if (this.data.activeTab === 'camera') {
      console.log('摄像头页面激活,检查组件状态');
      this.data.cameraList.forEach(camera => {
        console.log(`摄像头 ${camera.name} 状态:`, {
          id: camera.id,
          online: camera.online,
          hslUrl: camera.hslUrl,
          isLoadingUrl: camera.isLoadingUrl,
          urlError: camera.urlError,
          isPlaying: camera.isPlaying
        });
      });
    }
  },
  /**
@@ -52,9 +180,11 @@
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {
    // 下拉刷新时重新获取摄像头列表
    // 下拉刷新时重新获取数据
    if (this.data.activeTab === 'camera') {
      this.getCameraList();
    } else if (this.data.activeTab === 'weather' || this.data.activeTab === 'soil' || this.data.activeTab === 'fertilizer') {
      this.getAllDeviceInfo();
    }
    wx.stopPullDownRefresh();
  },
@@ -82,11 +212,134 @@
    this.setData({
      activeTab: tab
    });
    // 如果切换到摄像头选项卡,确保有数据
    if (tab === 'camera' && this.data.cameraList.length === 0) {
      this.getCameraList();
    }
    // 如果切换到气象站选项卡,确保有数据
    if (tab === 'weather' && this.data.weatherStationList.length === 0) {
      this.getAllDeviceInfo();
    }
    // 如果切换到土壤墒情站选项卡,确保有数据
    if (tab === 'soil' && this.data.soilStationList.length === 0) {
      this.getAllDeviceInfo();
    }
    // 如果切换到水肥机选项卡,确保有数据
    if (tab === 'fertilizer' && this.data.fertilizerStationList.length === 0) {
      this.getAllDeviceInfo();
    }
  },
  /**
   * 获取所有设备信息(气象站、土壤墒情站、水肥机)
   */
  getAllDeviceInfo() {
    const app = getApp();
    // 检查登录状态
    if (!app.globalData.isLoggedIn) {
      wx.showToast({
        title: '请先登录',
        icon: 'error'
      });
      return;
    }
    console.log('开始调用 /wx/mqtt/allSimple 接口获取设备信息');
    get({url: '/wx/mqtt/allSimple'})
      .then(response => {
        console.log('设备信息接口返回数据:', response);
        if (response.success && response.code === '0001') {
          const content = response.content;
          // 处理气象站数据
          if (content.weathers && content.weathers.length > 0) {
            const weatherStations = content.weathers.map(item => ({
              id: item.id,
              name: item.name,
              no: item.no,
              online: true, // 默认在线
              location: '甘肃省民勤县', // 默认位置
              lastUpdate: new Date().toLocaleString()
            }));
            this.setData({
              weatherStationList: weatherStations
            });
            // 默认选择第一个气象站
            if (weatherStations.length > 0) {
              this.selectWeatherStation(0);
            }
          }
          // 处理土壤墒情站数据
          if (content.soils && content.soils.length > 0) {
            const soilStations = content.soils.map(item => ({
              id: item.id,
              name: item.name,
              no: item.no,
              online: true, // 默认在线
              location: '甘肃省民勤县', // 默认位置
              lastUpdate: new Date().toLocaleString()
            }));
            this.setData({
              soilStationList: soilStations
            });
            // 默认选择第一个土壤墒情站
            if (soilStations.length > 0) {
              this.selectSoilStation(0);
            }
          }
          // 处理水肥机数据
          if (content.manures && content.manures.length > 0) {
            const fertilizerStations = content.manures.map(item => ({
              id: item.id,
              name: item.name,
              no: item.no,
              online: true // 默认在线
            }));
            this.setData({
              fertilizerStationList: fertilizerStations
            });
            // 默认选择第一个水肥机
            if (fertilizerStations.length > 0) {
              this.selectFertilizerStation(0);
            }
          }
          console.log('设备信息处理完成:', {
            weatherStations: this.data.weatherStationList,
            soilStations: this.data.soilStationList,
            fertilizerStations: this.data.fertilizerStationList
          });
        } else {
          console.error('获取设备信息失败:', response.msg);
          wx.showToast({
            title: response.msg || '获取设备信息失败',
            icon: 'none'
          });
        }
      })
      .catch(error => {
        console.error('调用设备信息接口失败:', error);
        wx.showToast({
          title: '获取设备信息失败',
          icon: 'error'
        });
      });
  },
  /**
@@ -94,7 +347,7 @@
   */
  getCameraList() {
    const app = getApp();
    // 检查登录状态
    if (!app.globalData.isLoggedIn) {
      wx.showToast({
@@ -108,410 +361,835 @@
      isLoading: true
    });
    // 模拟接口返回数据
    setTimeout(() => {
      const mockResponse = {
        "code": "0001",
        "content": {
          "itemTotal": 4,
          "obj": [
            {
              "id": "2025070715040300007",
              "name": "民勤01",
              "videoUrl4PcLive": "https://open.ys7.com/console/jssdk/pc.html?url=ezopen://open.ys7.com/FX6737162/1.live&accessToken=at.2o04glgs0q36cjugbvddqujz7tqrghx1-1ovr6lmf3k-03pij3c-304ziif7e&themeId=pcLive&env=&date=",
              "videoUrl4Security": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056878/1.live&themeId=security&date=",
              "videoUrl4Simple": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056878/1.live&themeId=simple&date=",
              "videoUrl4Standard": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056878/1.live&themeId=standard&date="
            },
            {
              "id": "2025070715040300008",
              "name": "民勤02",
              "videoUrl4PcLive": "https://open.ys7.com/console/jssdk/pc.html?url=ezopen://open.ys7.com/FY4056879/1.live&accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&themeId=pcLive&env=&date=",
              "videoUrl4Security": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056879/1.live&themeId=security&date=",
              "videoUrl4Simple": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056879/1.live&themeId=simple&date=",
              "videoUrl4Standard": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056879/1.live&themeId=standard&date="
            },
            {
              "id": "2025070715040300009",
              "name": "民勤03",
              "videoUrl4PcLive": "https://open.ys7.com/console/jssdk/pc.html?url=ezopen://open.ys7.com/FY4056880/1.live&accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&themeId=pcLive&env=&date=",
              "videoUrl4Security": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056880/1.live&themeId=security&date=",
              "videoUrl4Simple": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056880/1.live&themeId=simple&date=",
              "videoUrl4Standard": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056880/1.live&themeId=standard&date="
            },
            {
              "id": "2025070715040300010",
              "name": "民勤04",
              "videoUrl4PcLive": "https://open.ys7.com/console/jssdk/pc.html?url=ezopen://open.ys7.com/FY4056881/1.live&accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&themeId=pcLive&env=&date=",
              "videoUrl4Security": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056881/1.live&themeId=security&date=",
              "videoUrl4Simple": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056881/1.live&themeId=simple&date=",
              "videoUrl4Standard": "https://open.ys7.com/console/jssdk/pc.html?accessToken=at.87a8u4z04s3gom0o6i0cpgz35kuhu8xh-67xrfkiy90-0nnbl6z-r0v9mckp3&url=ezopen://open.ys7.com/FY4056881/1.live&themeId=standard&date="
            }
          ],
          "pageCurr": 1,
          "pageSize": 4,
          "pageTotal": 1
        },
        "msg": "请求成功",
        "success": true
      };
    // 调用真实接口获取摄像头列表
    this.getVideoListFromApi();
  },
      console.log('模拟接口返回数据:', mockResponse);
      if (mockResponse.success && mockResponse.code === '0001') {
        // 处理返回的摄像头数据
        const cameraList = mockResponse.content.obj.map(item => {
          // 从萤石云URL中提取设备信息并生成RTMP地址
          let rtmpUrl = '';
          if (item.videoUrl4PcLive) {
            // 提取设备序列号和通道号
            const ezopenMatch = item.videoUrl4PcLive.match(/ezopen:\/\/open\.ys7\.com\/([^\/]+)\/(\d+)\.live/);
            const tokenMatch = item.videoUrl4PcLive.match(/accessToken=([^&]+)/);
  /**
   * 从接口获取视频列表
   */
  getVideoListFromApi() {
    console.log('开始调用 /wx/video/all 接口获取视频列表');
    get({url:'/wx/video/all'} )
      .then(response => {
        console.log('接口返回数据:', response);
        if (response.success && response.code === '0001') {
          // 处理返回的摄像头数据
          const cameraList = response.content.map(item => {
            // 根据devNo生成RTMP URL
            const channelNo = 1; // 默认通道号
            const rtmpUrl = `rtmp://open.ys7.com/${item.devNo}/${channelNo}/live`;
            
            if (ezopenMatch && tokenMatch) {
              const deviceSerial = ezopenMatch[1]; // 设备序列号
              const channelNo = ezopenMatch[2];    // 通道号
              const accessToken = tokenMatch[1];   // 访问令牌
              // 生成RTMP地址
              rtmpUrl = `rtmp://open.ys7.com:1935/live/${deviceSerial}/${channelNo}?accessToken=${accessToken}`;
              // 备用HLS地址
              const hlsUrl = `https://open.ys7.com:443/live/${deviceSerial}/${channelNo}.m3u8?accessToken=${accessToken}`;
              console.log('生成的RTMP地址:', rtmpUrl);
              console.log('生成的HLS地址:', hlsUrl);
            }
          }
            console.log(`摄像头 ${item.name} 生成RTMP URL:`, rtmpUrl);
            return {
              id: item.id,
              name: item.name,
              onLine: true, // 默认在线,实际项目中可能需要额外的状态检查
              lastUpdate: new Date().toLocaleString(), // 当前时间作为最后更新时间
              isPlaying: false, // 视频播放状态
              hslUrl: rtmpUrl, // 使用生成的RTMP URL
              deviceSerial: item.devNo, // 使用接口返回的devNo
              isLoadingUrl: false, // URL加载状态
              urlError: false, // URL获取错误状态
              autoPlay: false, // 自动播放控制
              // 新增字段
              lng: item.lng, // 经度
              lat: item.lat, // 纬度
              accessToken: item.accessToken // 设备专用accessToken
            };
          });
          this.setData({
            cameraList: cameraList,
            isLoading: false
          });
          console.log('处理后的摄像头列表:', cameraList);
          
          return {
            id: item.id,
            name: item.name,
            online: true, // 默认在线,实际项目中可能需要额外的状态检查
            thumbnail: '/images/camera-thumb1.jpg', // 默认缩略图
            lastUpdate: new Date().toLocaleString(), // 当前时间作为最后更新时间
            isPlaying: false, // 视频播放状态
            rtmpUrl: rtmpUrl, // RTMP流地址
            videoUrl4PcLive: item.videoUrl4PcLive, // 原始PC播放地址
            videoUrl4Security: item.videoUrl4Security,
            videoUrl4Simple: item.videoUrl4Simple,
            videoUrl4Standard: item.videoUrl4Standard
          };
        });
        this.setData({
          cameraList: cameraList,
          isLoading: false
        });
        console.log('处理后的摄像头列表:', cameraList);
      } else {
        console.error('获取摄像头列表失败:', mockResponse.msg);
        this.setData({
          isLoading: false
        });
        wx.showToast({
          title: mockResponse.msg || '获取摄像头列表失败',
          icon: 'none'
        });
      }
    }, 1000); // 模拟网络延迟1秒
  },
  /**
   * 播放视频
   */
  playVideo(e) {
    const camera = e.currentTarget.dataset.camera;
    console.log('播放摄像头:', camera.name);
    if (!camera.online) {
      wx.showToast({
        title: '摄像头离线',
        icon: 'error'
      });
      return;
    }
    // 检查视频URL是否有效
    if (!camera.videoUrl4PcLive) {
      wx.showToast({
        title: '视频地址无效',
        icon: 'error'
      });
      return;
    }
    console.log('视频URL:', camera.videoUrl4PcLive);
    // 更新视频播放状态
    const cameraList = this.data.cameraList.map(item => {
      if (item.id === camera.id) {
        return { ...item, isPlaying: true };
      }
      return item;
    });
    this.setData({
      cameraList: cameraList
    });
    // 延迟一下让live-player组件更新
    setTimeout(() => {
      console.log('开始播放直播:', camera.videoUrl4PcLive);
    }, 100);
  },
  /**
   * 暂停视频
   */
  pauseVideo(e) {
    const camera = e.currentTarget.dataset.camera;
    console.log('暂停摄像头:', camera.name);
    // 更新视频播放状态
    const cameraList = this.data.cameraList.map(item => {
      if (item.id === camera.id) {
        return { ...item, isPlaying: false };
      }
      return item;
    });
    this.setData({
      cameraList: cameraList
    });
  },
  /**
   * 停止视频
   */
  stopVideo(e) {
    const camera = e.currentTarget.dataset.camera;
    console.log('停止摄像头:', camera.name);
    // 更新视频播放状态
    const cameraList = this.data.cameraList.map(item => {
      if (item.id === camera.id) {
        return { ...item, isPlaying: false };
      }
      return item;
    });
    this.setData({
      cameraList: cameraList
    });
  },
  /**
   * 测试视频URL
   */
  testVideoUrl(e) {
    const camera = e.currentTarget.dataset.camera;
    console.log('测试视频URL:', camera.name);
    console.log('原始URL:', camera.videoUrl4PcLive);
    console.log('RTMP URL:', camera.rtmpUrl);
    // 显示URL信息
    wx.showModal({
      title: '视频URL信息',
      content: `摄像头: ${camera.name}\n原始URL: ${camera.videoUrl4PcLive}\nRTMP URL: ${camera.rtmpUrl}`,
      showCancel: false,
      confirmText: '确定'
    });
  },
  /**
   * 直播播放器状态变化
   */
  onLivePlayerStateChange(e) {
    const camera = e.currentTarget.dataset.camera;
    console.log('直播播放器状态变化:', camera.name, e.detail);
    const { code } = e.detail;
    // 显示状态信息给用户
    let statusText = '';
    let isPlaying = false;
    switch (code) {
      case 2001:
        statusText = '已经连接服务器';
        break;
      case 2002:
        statusText = '已经连接 RTMP 服务器,开始拉流';
        isPlaying = true;
        break;
      case 2003:
        statusText = '网络接收到首个视频数据包(IDR)';
        isPlaying = true;
        break;
      case 2004:
        statusText = '视频播放开始';
        isPlaying = true;
        break;
      case 2005:
        statusText = '视频播放进度';
        isPlaying = true;
        break;
      case 2006:
        statusText = '视频播放结束';
        isPlaying = false;
        break;
      case 2007:
        statusText = '视频播放Loading';
        isPlaying = true;
        break;
      case 2008:
        statusText = '解码器启动';
        isPlaying = true;
        break;
      case 2009:
        statusText = '视频分辨率改变';
        isPlaying = true;
        break;
      case 2101:
        statusText = '网络断连,且经多次重连抢救无效';
        isPlaying = false;
        break;
      case 2102:
        statusText = '获取加速拉流地址失败';
        isPlaying = false;
        break;
      case 2103:
        statusText = '当前视频帧解码失败';
        isPlaying = false;
        break;
      case 2104:
        statusText = '网络断连, 已启动自动重连';
        isPlaying = false;
        break;
      case 2105:
        statusText = '网络来断连, 且经多次重连抢救无效';
        isPlaying = false;
        break;
      case 2106:
        statusText = '网络来断连, 且经多次重连抢救无效';
        isPlaying = false;
        break;
      default:
        statusText = `未知状态码: ${code}`;
        break;
    }
    console.log(`摄像头 ${camera.name} 状态: ${statusText}`);
    // 更新播放状态
    const cameraList = this.data.cameraList.map(item => {
      if (item.id === camera.id) {
        return { ...item, isPlaying: isPlaying };
      }
      return item;
    });
    this.setData({
      cameraList: cameraList
    });
  },
  /**
   * 直播播放器网络状态
   */
  onLivePlayerNetStatus(e) {
    const camera = e.currentTarget.dataset.camera;
    console.log('直播播放器网络状态:', camera.name, e.detail);
  },
  /**
   * 直播播放器错误
   */
  onLivePlayerError(e) {
    const camera = e.currentTarget.dataset.camera;
    console.error('直播播放器错误:', camera.name, e.detail);
    wx.showToast({
      title: '直播播放失败',
      icon: 'error'
    });
    // 更新播放状态
    const cameraList = this.data.cameraList.map(item => {
      if (item.id === camera.id) {
        return { ...item, isPlaying: false };
      }
      return item;
    });
    this.setData({
      cameraList: cameraList
    });
  },
  /**
   * 全屏播放视频
   */
  fullscreenVideo(e) {
    const camera = e.currentTarget.dataset.camera;
    console.log('全屏播放:', camera.name);
    if (!camera.online) {
      wx.showToast({
        title: '摄像头离线',
        icon: 'error'
      });
      return;
    }
    // 使用PC直播URL进行全屏播放
    if (camera.videoUrl4PcLive) {
      console.log('全屏视频URL:', camera.videoUrl4PcLive);
      wx.showModal({
        title: '全屏播放',
        content: `即将全屏播放 ${camera.name} 的视频流`,
        confirmText: '开始播放',
        success: (res) => {
          if (res.confirm) {
            // 这里可以跳转到全屏视频播放页面
            wx.showToast({
              title: '正在加载全屏视频...',
              icon: 'loading'
            });
            // 模拟全屏视频加载
            setTimeout(() => {
              wx.showToast({
                title: '全屏播放中',
                icon: 'success'
              });
            }, 1500);
          }
          // 由于现在直接使用RTMP URL,不需要再调用萤石云API获取播放地址
          // this.batchGetHlsUrls(cameraList);
        } else {
          console.error('获取摄像头列表失败:', response.msg);
          this.setData({
            isLoading: false
          });
          wx.showToast({
            title: response.msg || '获取摄像头列表失败',
            icon: 'none'
          });
        }
      })
      .catch(error => {
        console.error('调用接口失败:', error);
        this.setData({
          isLoading: false
        });
      });
  },
  /**
   * 获取气象站列表(兼容性方法,现在调用统一接口)
   */
  getWeatherStationList() {
    console.log('获取气象站列表(调用统一接口)');
    this.getAllDeviceInfo();
  },
  /**
   * 选择气象站
   */
  selectWeatherStation(index) {
    const weatherStation = this.data.weatherStationList[index];
    if (!weatherStation) return;
    console.log('选择气象站:', weatherStation.name);
    // 获取该气象站的详细数据
    this.getWeatherStationData(weatherStation.id);
    this.setData({
      selectedWeatherStationIndex: index
    });
  },
  /**
   * 获取气象站详细数据
   */
  getWeatherStationData(stationId) {
    console.log('获取气象站数据:', stationId);
    // 调用真实接口获取气象站详细信息
    this.getWeatherLastData(stationId);
  },
  /**
   * 调用 /wx/mqttLast/oneWeatherLast 接口获取气象站详细信息
   */
  getWeatherLastData(weatherId) {
    console.log('开始调用 /wx/mqttLast/oneWeatherLast 接口获取气象站详细信息');
    console.log('气象站ID:', weatherId);
    get({url: `/wx/mqttLast/oneWeatherLast?weatherId=${weatherId}`})
      .then(response => {
        console.log('气象站详细信息接口返回数据:', response);
        if (response.success && response.code === '0001') {
          const content = response.content;
          // 处理接口返回的气象站数据
          const weatherData = {
            id: content.id,
            weatherId: content.weatherId,
            weatherName: content.weatherName || '气象站',
            dt: content.dt,
            // 气象数据
            temperature: content.airTemperature, // 空气温度
            humidity: content.airHumidity, // 空气湿度
            uv: content.ultraviolet, // 紫外线
            light: content.lightIntensity, // 光照强度
            rainfall: content.rainfall, // 雨量
            windSpeed: content.windSpeed, // 风速
            windDirection: content.windDirectionStr, // 风向描述
            windDirectionAngle: content.windDirection, // 风向角度
            // 在线状态
            onLine: content.onLine,
            // 格式化显示数据
            lastUpdate: content.dt || new Date().toLocaleString()
          };
          this.setData({
            currentWeatherStation: weatherData
          });
          console.log('处理后的气象站数据:', weatherData);
        } else {
          console.error('获取气象站详细信息失败:', response.msg);
          wx.showToast({
            title: response.msg || '获取气象站详细信息失败',
            icon: 'none'
          });
          // 如果接口调用失败,使用模拟数据作为备选
          this.setMockWeatherData(stationId);
        }
      })
      .catch(error => {
        console.error('调用气象站详细信息接口失败:', error);
        wx.showToast({
          title: '获取气象站详细信息失败',
          icon: 'error'
        });
        // 如果接口调用失败,使用模拟数据作为备选
        this.setMockWeatherData(stationId);
      });
  },
  /**
   * 设置模拟气象站数据(作为接口调用失败的备选方案)
   */
  setMockWeatherData(stationId) {
    console.log('使用模拟气象站数据:', stationId);
    const mockWeatherData = {
      id: stationId,
      weatherId: stationId,
      weatherName: '气象站',
      dt: new Date().toLocaleString(),
      temperature: 0.0,
      humidity: 0.0,
      uv: 0,
      light: 0,
      rainfall: 0.00,
      windSpeed: 0.00,
      windDirection: '北',
      windDirectionAngle: 0,
      onLine: false,
      online: true,
      lastUpdate: new Date().toLocaleString()
    };
    this.setData({
      currentWeatherStation: mockWeatherData
    });
  },
  /**
   * 气象站选择改变
   */
  onWeatherStationChange(e) {
    const index = e.detail.value;
    this.selectWeatherStation(index);
  },
  /**
   * 刷新气象数据
   */
  refreshWeatherData() {
    if (!this.data.currentWeatherStation) {
      wx.showToast({
        title: '请先选择气象站',
        icon: 'none'
      });
      return;
    }
    console.log('刷新气象数据');
    wx.showLoading({
      title: '刷新中...'
    });
    // 重新调用接口获取最新数据
    this.getWeatherLastData(this.data.currentWeatherStation.weatherId || this.data.currentWeatherStation.id);
    wx.hideLoading();
    wx.showToast({
      title: '刷新成功',
      icon: 'success'
    });
  },
  /**
   * 选择土壤墒情站
   */
  selectSoilStation(index) {
    const soilStation = this.data.soilStationList[index];
    if (!soilStation) return;
    console.log('选择土壤墒情站:', soilStation.name);
    // 获取该土壤墒情站的详细数据
    this.getSoilStationData(soilStation.id);
    this.setData({
      selectedSoilStationIndex: index
    });
  },
  /**
   * 获取土壤墒情站详细数据
   */
  getSoilStationData(stationId) {
    console.log('获取土壤墒情站数据:', stationId);
    // 调用真实接口获取土壤墒情站详细信息
    this.getSoilLastData(stationId);
  },
  /**
   * 调用 /wx/mqttLast/oneSoilLast 接口获取土壤墒情站详细信息
   */
  getSoilLastData(soilId) {
    console.log('开始调用 /wx/mqttLast/oneSoilLast 接口获取土壤墒情站详细信息');
    console.log('土壤墒情站ID:', soilId);
    get({url: `/wx/mqttLast/oneSoilLast?soilId=${soilId}`})
      .then(response => {
        console.log('土壤墒情站详细信息接口返回数据:', response);
        if (response.success && response.code === '0001') {
          const content = response.content;
          // 处理接口返回的土壤墒情站数据
          const soilData = {
            id: content.id,
            soilId: content.soilId,
            soilName: content.soilName || '土壤墒情站',
            dt: content.dt,
            // 5层土壤湿度数据
            soilHumidity1: content.soilHumidity1,
            soilHumidity2: content.soilHumidity2,
            soilHumidity3: content.soilHumidity3,
            soilHumidity4: content.soilHumidity4,
            soilHumidity5: content.soilHumidity5,
            // 5层土壤温度数据
            soilTemperature1: content.soilTemperature1,
            soilTemperature2: content.soilTemperature2,
            soilTemperature3: content.soilTemperature3,
            soilTemperature4: content.soilTemperature4,
            soilTemperature5: content.soilTemperature5,
            // 在线状态
            onLine: content.onLine,
            // 计算在线状态
            online: content.onLine === 1,
            // 格式化显示数据
            lastUpdate: content.dt || new Date().toLocaleString()
          };
          this.setData({
            currentSoilStation: soilData
          });
          console.log('处理后的土壤墒情站数据:', soilData);
        } else {
          console.error('获取土壤墒情站详细信息失败:', response.msg);
          wx.showToast({
            title: response.msg || '获取土壤墒情站详细信息失败',
            icon: 'none'
          });
          // 如果接口调用失败,使用模拟数据作为备选
          this.setMockSoilData(stationId);
        }
      })
      .catch(error => {
        console.error('调用土壤墒情站详细信息接口失败:', error);
        wx.showToast({
          title: '获取土壤墒情站详细信息失败',
          icon: 'error'
        });
        // 如果接口调用失败,使用模拟数据作为备选
        this.setMockSoilData(stationId);
      });
  },
  /**
   * 设置模拟土壤墒情站数据(作为接口调用失败的备选方案)
   */
  setMockSoilData(stationId) {
    console.log('使用模拟土壤墒情站数据:', stationId);
    const mockSoilData = {
      id: stationId,
      soilId: stationId,
      soilName: '土壤墒情站',
      dt: new Date().toLocaleString(),
      // 5层土壤湿度数据
      soilHumidity1: 0.00,
      soilHumidity2: 0.00,
      soilHumidity3: 0.00,
      soilHumidity4: 0.00,
      soilHumidity5: 0.00,
      // 5层土壤温度数据
      soilTemperature1: 0.00,
      soilTemperature2: 0.00,
      soilTemperature3: 0.00,
      soilTemperature4: 0.00,
      soilTemperature5: 0.00,
      // 在线状态
      onLine: false,
      lastUpdate: new Date().toLocaleString()
    };
    this.setData({
      currentSoilStation: mockSoilData
    });
  },
  /**
   * 土壤墒情站选择改变
   */
  onSoilStationChange(e) {
    const index = e.detail.value;
    this.selectSoilStation(index);
  },
  /**
   * 刷新土壤墒情数据
   */
  refreshSoilData() {
    if (!this.data.currentSoilStation) {
      wx.showToast({
        title: '请先选择土壤墒情站',
        icon: 'none'
      });
      return;
    }
    console.log('刷新土壤墒情数据');
    wx.showLoading({
      title: '刷新中...'
    });
    // 重新调用接口获取最新数据
    this.getSoilLastData(this.data.currentSoilStation.soilId || this.data.currentSoilStation.id);
    wx.hideLoading();
    wx.showToast({
      title: '刷新成功',
      icon: 'success'
    });
  },
  /**
   * 获取水肥机列表(兼容性方法,现在调用统一接口)
   */
  getFertilizerStationList() {
    console.log('获取水肥机列表(调用统一接口)');
    this.getAllDeviceInfo();
  },
  /**
   * 选择水肥机
   */
  selectFertilizerStation(index) {
    const fertilizerStation = this.data.fertilizerStationList[index];
    if (!fertilizerStation) return;
    console.log('选择水肥机:', fertilizerStation.name);
    // 获取该水肥机的详细数据
    this.getFertilizerStationData(fertilizerStation.id);
    this.setData({
      selectedFertilizerStationIndex: index
    });
  },
  /**
   * 获取水肥机详细数据
   */
  getFertilizerStationData(stationId) {
    console.log('获取水肥机数据:', stationId);
    // 调用真实接口获取水肥机详细信息
    this.getManureLastData(stationId);
  },
  /**
   * 调用 /wx/mqttLast/oneManureLast 接口获取水肥机详细信息
   */
  getManureLastData(manureId) {
    console.log('开始调用 /wx/mqttLast/oneManureLast 接口获取水肥机详细信息');
    console.log('水肥机ID:', manureId);
    get({url: `/wx/mqttLast/oneManureLast?manureId=${manureId}`})
      .then(response => {
        console.log('水肥机详细信息接口返回数据:', response);
        if (response.success && response.code === '0001') {
          const content = response.content;
          // 处理接口返回的水肥机数据
          const fertilizerData = {
            id: content.id,
            manureId: content.manureId,
            manureName: content.manureName || '水肥机',
            dt: content.dt,
            alarm: content.alarm,
            // 搅拌运行状态
            stirRunning1: content.stirRunning1,
            // 注肥运行状态
            injectRunning: content.injectRunning,
            // 流量和时间数据
            manureFlow: content.manureFlow,
            manureTime: content.manureTime,
            stirTime: content.stirTime,
            stirDuration: content.stirDuration,
            injectDuration: content.injectDuration,
            // 在线状态
            onLine: content.onLine,
            // 计算运行状态
            mixingEnabled: content.stirRunning1 === 1,
            fertilizingEnabled: content.injectRunning === 1,
            // 格式化显示数据
            lastUpdate: content.dt || new Date().toLocaleString()
          };
          this.setData({
            currentFertilizerStation: fertilizerData
          });
          console.log('处理后的水肥机数据:', fertilizerData);
        } else {
          console.error('获取水肥机详细信息失败:', response.msg);
          wx.showToast({
            title: response.msg || '获取水肥机详细信息失败',
            icon: 'none'
          });
          // 如果接口调用失败,使用模拟数据作为备选
          this.setMockFertilizerData(stationId);
        }
      })
      .catch(error => {
        console.error('调用水肥机详细信息接口失败:', error);
        wx.showToast({
          title: '获取水肥机详细信息失败',
          icon: 'error'
        });
        // 如果接口调用失败,使用模拟数据作为备选
        this.setMockFertilizerData(stationId);
      });
  },
  /**
   * 设置模拟水肥机数据(作为接口调用失败的备选方案)
   */
  setMockFertilizerData(stationId) {
    console.log('使用模拟水肥机数据:', stationId);
    const mockFertilizerData = {
      id: stationId,
      manureId: stationId,
      manureName: '水肥机',
      dt: new Date().toLocaleString(),
      alarm: 0,
      stirRunning1: 0,
      injectRunning: 0,
      manureFlow: 0.00,
      manureTime: 0,
      stirTime: 0,
      stirDuration: 300,
      injectDuration: 300,
      onLine: false,
      mixingEnabled: false,
      fertilizingEnabled: false,
      lastUpdate: new Date().toLocaleString()
    };
    this.setData({
      currentFertilizerStation: mockFertilizerData
    });
  },
  /**
   * 水肥机选择改变
   */
  onFertilizerStationChange(e) {
    const index = e.detail.value;
    this.selectFertilizerStation(index);
  },
  /**
   * 刷新水肥机数据
   */
  refreshFertilizerData() {
    if (!this.data.currentFertilizerStation) {
      wx.showToast({
        title: '请先选择水肥机',
        icon: 'none'
      });
      return;
    }
    console.log('刷新水肥机数据');
    wx.showLoading({
      title: '刷新中...'
    });
    // 重新调用接口获取最新数据
    this.getManureLastData(this.data.currentFertilizerStation.manureId || this.data.currentFertilizerStation.id);
    wx.hideLoading();
    wx.showToast({
      title: '刷新成功',
      icon: 'success'
    });
  },
  /**
   * 切换搅拌开关
   */
  toggleMixing(e) {
    const enabled = e.detail.value;
    console.log('搅拌开关:', enabled ? '开启' : '关闭');
    if (!this.data.currentFertilizerStation) return;
    // 更新搅拌状态
    const currentStation = {
      ...this.data.currentFertilizerStation
    };
    currentStation.mixingEnabled = enabled;
    // 更新具体的搅拌运行状态(这里假设只控制第一个搅拌器)
    currentStation.stirRunning1 = enabled ? 1 : 0;
    this.setData({
      currentFertilizerStation: currentStation
    });
    // 显示操作结果
    wx.showToast({
      title: enabled ? '搅拌已开启' : '搅拌已关闭',
      icon: 'success'
    });
  },
  /**
   * 切换注肥开关
   */
  toggleFertilizing(e) {
    const enabled = e.detail.value;
    console.log('注肥开关:', enabled ? '开启' : '关闭');
    if (!this.data.currentFertilizerStation) return;
    // 更新注肥状态
    const currentStation = {
      ...this.data.currentFertilizerStation
    };
    currentStation.fertilizingEnabled = enabled;
    // 更新注肥运行状态
    currentStation.injectRunning = enabled ? 1 : 0;
    this.setData({
      currentFertilizerStation: currentStation
    });
    // 显示操作结果
    wx.showToast({
      title: enabled ? '注肥已开启' : '注肥已关闭',
      icon: 'success'
    });
  },
  /**
   * 手动重试获取播放地址
   */
  retryGetHlsUrl(e) {
    const camera = e.currentTarget.dataset.camera;
    if (!camera) {
      console.error('重试失败:摄像头信息不完整');
      wx.showToast({
        title: '摄像头信息不完整',
        icon: 'error'
      });
      return;
    }
    console.log('=== 手动重试获取播放地址 ===');
    console.log(`摄像头: ${camera.name}, ID: ${camera.id}`);
    console.log('当前状态:', {
      online: camera.online,
      hslUrl: camera.hslUrl,
      isLoadingUrl: camera.isLoadingUrl,
      urlError: camera.urlError
    });
    // 重置错误状态并重新获取
    this.updateCameraUrlLoadingState(camera.id, false, false);
    this.getHlsUrlForCamera(camera);
  },
  /**
   * 更新摄像头URL加载状态
   */
  updateCameraUrlLoadingState(cameraId, isLoading, hasError) {
    console.log('=== 更新摄像头URL加载状态 ===');
    console.log('参数:', { cameraId, isLoading, hasError });
    const cameraList = this.data.cameraList.map(item => {
      if (item.id === cameraId) {
        const updatedItem = {
          ...item,
          isLoadingUrl: isLoading,
          urlError: hasError
        };
        console.log(`摄像头 ${cameraId} 状态更新:`, {
          name: updatedItem.name,
          isLoadingUrl: updatedItem.isLoadingUrl,
          urlError: updatedItem.urlError
        });
        return updatedItem;
      }
      return item;
    });
    this.setData({
      cameraList: cameraList
    });
    console.log('状态更新完成,当前摄像头列表:', cameraList);
  },
  /**
   * 为单个摄像头获取HLS播放地址
   */
  getHlsUrlForCamera(camera) {
    if (!camera || !camera.deviceSerial) {
      console.error('摄像头信息不完整:', camera);
      return;
    }
    console.log('=== 获取播放地址开始 ===');
    console.log(`摄像头: ${camera.name}, ID: ${camera.id}, 设备序列号: ${camera.deviceSerial}`);
    console.log('当前accessToken:', this.data.accessToken);
    // 更新加载状态
    this.updateCameraUrlLoadingState(camera.id, true, false);
    // 调用萤石云API获取播放地址
    this.getHlsUrl(this.data.accessToken, camera.deviceSerial, camera.id);
  },
  /**
   * ezplayer错误处理
   */
  handleError(e) {
    console.log('=== ezplayer 错误处理 ===');
    console.log('错误事件详情:', e);
    console.log('错误详情:', e.detail);
    // 获取摄像头信息
    const cameraId = e.currentTarget.id;
    console.log('出错的摄像头ID:', cameraId);
    // 查找对应的摄像头
    const camera = this.data.cameraList.find(item => `ezplayer-${item.id}` === cameraId);
    if (camera) {
      console.log('出错的摄像头信息:', camera);
      // 更新错误状态
      this.updateCameraUrlLoadingState(camera.id, false, true);
      // 显示错误提示
      wx.showToast({
        title: '视频播放出错',
        icon: 'error',
        duration: 2000
      });
    } else {
      wx.showToast({
        title: '全屏视频地址无效',
        icon: 'error'
      });
      console.error('未找到对应的摄像头:', cameraId);
    }
  },
  /**
   * 摄像头设置
   * ezplayer控制事件
   */
  cameraSettings(e) {
    const camera = e.currentTarget.dataset.camera;
    console.log('摄像头设置:', camera.name);
  onControlEvent(e) {
    console.log('=== ezplayer 控制事件 ===');
    console.log('控制事件详情:', e);
    console.log('事件类型:', e.type);
    console.log('事件数据:', e.detail);
    
    wx.showActionSheet({
      itemList: ['云台控制', '录像设置', '画质调节', '报警设置'],
      success: (res) => {
        const actions = ['云台控制', '录像设置', '画质调节', '报警设置'];
        wx.showToast({
          title: `${actions[res.tapIndex]}功能开发中`,
          icon: 'none'
        });
    // 获取摄像头信息
    const cameraId = e.currentTarget.id;
    console.log('事件来源摄像头ID:', cameraId);
    // 查找对应的摄像头
    const camera = this.data.cameraList.find(item => `ezplayer-${item.id}` === cameraId);
    if (camera) {
      console.log('事件来源摄像头信息:', camera);
      // 根据事件类型处理
      switch (e.type) {
        case 'play':
          console.log('视频开始播放');
          break;
        case 'pause':
          console.log('视频暂停播放');
          break;
        case 'ended':
          console.log('视频播放结束');
          break;
        case 'error':
          console.log('视频播放错误');
          this.updateCameraUrlLoadingState(camera.id, false, true);
          break;
      default:
          console.log('未知事件类型:', e.type);
      }
    } else {
      console.error('未找到对应的摄像头:', cameraId);
    }
  },
  /**
   * 更新摄像头播放状态
   */
  updateCameraPlayState(cameraId, isPlaying) {
    console.log('=== 更新摄像头播放状态 ===');
    console.log('参数:', { cameraId, isPlaying });
    const cameraList = this.data.cameraList.map(item => {
      if (item.id === cameraId) {
        const updatedItem = {
          ...item,
          isPlaying: isPlaying
        };
        console.log(`摄像头 ${cameraId} 播放状态更新:`, {
          name: updatedItem.name,
          isPlaying: updatedItem.isPlaying
        });
        return updatedItem;
      }
      return item;
    });
  }
})
    this.setData({
      cameraList: cameraList
    });
    console.log('播放状态更新完成');
  },
})