HarmonyOS 鸿蒙Next中桥接C语言使用Modbus控制的问题

HarmonyOS 鸿蒙Next中桥接C语言使用Modbus控制的问题 桥接c/cpp  这个文件和原理不太懂  能不能详细说一下
比如我要往对应192.168.1.200 里面   M7000 寄存器写一个    1或者0  
根据之前的案例看不太懂 跑不通
问题1每个文件以及里面的调用逻辑太复杂  看不懂

cke_5499.png

这里文件调用顺序是什么  ?  index.d.ts->brdgeModbus.app->napi_init.cpp  吗??cke_5910.png

cke_16417.png

cke_17167.png

cke_18037.png

cke_47568.png上传了CPP压缩包(不知道为什么zip不能上传 ,改成zip解压)

以下是调用代码

onPageShow(): void {
//开启一个显示时间的定时器
//每次页面显示时候开启定时器,参数1是调用更新时间的函数,1000是1000毫秒,后面还需要onPageHide配合实现页面不显示就不用定时器更新时间节省资源
clearInterval(this.timeID) //启动的时候先清一次
this.timeID = setInterval(() => {
  this.updateTimeDate()
  getContext().eventHub.emit('传连接状态')//每一秒钟发送一个检测有没有连接的更新请求
}, 1000) //setInterval 整体拿到的是一个timeid,下面要用

// 心跳包定时器(每5秒发送一次心跳)
setInterval(() => {
  // 发送心跳包到M7000寄存器
  try {
    this.sendHeartbeat()
  } catch (error) {
    console.error('心跳包定时器执行异常:', error);
  }
}, 5000)

//连接图标
//注册了一个监听事件,来了'传ip和port参数值'这个消息就执行后面的函数
getContext().eventHub.on('传ip和port参数值', (velue:object) => {
  this.ip = velue['ip'];
  this.port = velue['port']  //把高级设置里面的ip和port 传过来
  try {
    this.status = testNapi.init_connect(this.ip,Number(this.port))  //modbus初始化
    console.info('监听到了传ip和port参数值',velue['ip'],velue['port'])
  } catch (error) {
    console.error('初始化连接时发生异常:', error);
    this.status = -1;
  }
})

try {
  this.status= testNapi.init_connect(this.ip,Number(this.port))  //状态
} catch (error) {
  console.error('初始化连接时发生异常:', error);
  this.status = -1;
}


// let  status = testNapi.init_connect(this.ip,Number(this.port))  //modbus初始化

// testNapi.init_connect(this.ip,Number(this.port)) 调用文件顺序:
// ETS层调用 (入口)
// 文件: /Users/MD/DevEcoStudioProjects/BendOS_Local/entry/src/main/ets/pages/MainPage.ets
// 代码: import testNapi from 'libentry.so'
// 调用: testNapi.init_connect(this.ip,Number(this.port))
// Node-API桥接层
// 文件: /Users/MD/DevEcoStudioProjects/BendOS_Local/entry/src/main/cpp/modbus/bridgeModbus.cpp
// 函数: init_connect (N-API包装函数)
// 当前实现: 临时返回成功(1),未实际调用底层函数
// 头文件声明
// 文件: /Users/MD/DevEcoStudioProjects/BendOS_Local/entry/src/main/cpp/include/modbus/main.h
// 声明: int modbusInitConnect(const char* ip,int port);
// 实际实现层
// 文件: /Users/MD/DevEcoStudioProjects/BendOS_Local/entry/src/main/cpp/src/modbus/main.c
// 函数: modbusInitConnect (实际的Modbus连接实现)
// 功能: 创建Modbus TCP连接,设置参数,连接设备等
// 类型定义
// 文件: /Users/MD/DevEcoStudioProjects/BendOS_Local/entry/src/main/cpp/types/libentry/index.d.ts
// 定义: export const init_connect: (ip: string, b: number) => number;
// 构建配置
// 文件: /Users/MD/DevEcoStudioProjects/BendOS_Local/entry/src/main/cpp/modbus/CMakeLists.txt
// 配置: 定义如何编译链接生成libentry.so
// 注意: bridgeModbus.cpp中的init_connect函数是临时实现,直接返回成功(1),并未实际调用test_main.c中的modbusInitConnect函数。如果需要实际的Modbus连接功能,需要修改bridgeModbus.cpp中的init_connect函数来调用modbusInitConnect。


//注册了一个监听事件,来了'传连接状态'这个消息就执行后面的函数
getContext().eventHub.on('传连接状态', () => {
  console.info('监听到了传连接状态')
  try {
    // 简化连接检查逻辑
    // 只有在当前未连接时才尝试重新连接
    if (!this.rightTopIconList[0].isSelect) {
      this.status = testNapi.init_connect(this.ip,Number(this.port))  //modbus初始化
      if(this.status===1){
        this.rightTopIconList[0].isSelect =  true;  //已连接就更新这个rightTopIconList[0]的 状态 isSelect是 @Trace修饰的
        console.info(`modbus已连接`, `ip:${this.ip}`, `port:${this.port}`);
      }else {
        this.rightTopIconList[0].isSelect =  false;
        console.error(`'modbus未连接,需要重新连接`, `status:${this.status}`, `ip:${this.ip}`, `port:${this.port}`);
      }
    } else {
      console.info('当前已连接,跳过重复连接检查')
    }
  } catch (error) {
    console.error('连接状态检查时发生异常:', error);
    // 发生异常,标记为未连接
    this.rightTopIconList[0].isSelect = false;
  }
})

}

// 发送心跳包到M7000寄存器
private sendHeartbeat() {
// 检查是否已连接
if (!this.rightTopIconList[0].isSelect) {
  console.info('设备未连接,跳过心跳包发送');
  return;
}

// 计算M继电器的Modbus线圈地址 = 0x0800 + 继电器编号    M7000寄存器地址 = 0x0800 + 继电器编号  modbus要的是绝对地址
const coilAddr = 0x0800 + this.HEARTBEAT_REGISTER;

// 使用定义的常量发送心跳包
console.info(`发送心跳包到M${this.HEARTBEAT_REGISTER}寄存器(线圈地址: ${coilAddr}),值为${this.HEARTBEAT_VALUE}`)

// 调用C++层的函数来实际发送心跳包
try {
  this.heartbeatResult = testNapi.send_heartbeat(coilAddr, this.HEARTBEAT_VALUE);
  if (this.heartbeatResult === 1) {
    console.info('心跳包发送成功');
    // 心跳包发送成功,确保连接状态为已连接
    this.rightTopIconList[0].isSelect = true;
  } else {
    console.error('心跳包发送失败,返回值:', this.heartbeatResult);
    // 心跳包发送失败,标记为未连接
    this.rightTopIconList[0].isSelect = false;
  }
} catch (error) {
  console.error('发送心跳包时发生异常:', error);
  // 发生异常,标记为未连接
  this.rightTopIconList[0].isSelect = false;
}
}

最终效果是没有反应    如果可以 可以试一下在两个电脑上一个客户端  一个服务端  试一下 


更多关于HarmonyOS 鸿蒙Next中桥接C语言使用Modbus控制的问题的实战教程也可以访问 https://www.itying.com/category-93-b0.html

4 回复

开发者您好,可以采取以下方式解决:

【解决方案】 HarmonyOS创建的Native工程目录结构参考文档C++工程目录结构

根据您的问题回答如下:

  1. ArkTS调用Native顺序:ArkTS层(so导入以及调用)->index.d.ts(头文件)->napi_init.cpp(接口暴露)->具体的功能实现函数;

  2. napi_init.cpp文件内的函数暴露出去给到index.d.ts文件;

  3. ArkTS侧调用Native侧的函数:

Native工程编译运行时,Native侧代码最终会以so的形式被ArkTS侧调用,ArkTS侧的导入形式如下:

import ${抽象名} from '${so名}'

具体NAPI相关使用可以参考NDK开发导读使用Node-API实现跨语言交互开发流程

更多关于HarmonyOS 鸿蒙Next中桥接C语言使用Modbus控制的问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


代码参考

// 定义类型接口
interface IpPortConfig {
  ip: string
  port: number
}

// 假设这是你的页面类
class MainPage {
  // 状态变量
  private ip: string = '192.168.1.100'  // 默认IP
  private port: number = 502  // 默认端口
  private status: number = -1
  @Trace private rightTopIconList: Array<any> = [{ isSelect: false }]

  // 定时器ID
  private timeID: number = -1
  private heartbeatTimerID: number = -1

  // 心跳包相关常量
  private readonly HEARTBEAT_REGISTER: number = 7000
  private readonly HEARTBEAT_VALUE: number = 1
  private heartbeatResult: number = -1

  // 事件处理器绑定(箭头函数保持this上下文)
  private handleIpPortUpdate = (value: IpPortConfig): void => {
    console.info('收到IP和Port更新:', value.ip, value.port)

    this.ip = value.ip
    this.port = value.port

    try {
      // 断开旧连接(如果有相关API)
      // testNapi.disconnect()

      // 使用新的IP和Port初始化连接
      this.status = testNapi.init_connect(this.ip, Number(this.port))

      if (this.status === 1) {
        this.rightTopIconList[0].isSelect = true
        console.info('使用新配置连接成功', `ip:${this.ip}`, `port:${this.port}`)
      } else {
        this.rightTopIconList[0].isSelect = false
        console.error('使用新配置连接失败', `status:${this.status}`)
      }
    } catch (error) {
      console.error('初始化连接时发生异常:', error)
      this.status = -1
      this.rightTopIconList[0].isSelect = false
    }
  }

  /**
   * 组件即将出现 - 生命周期函数
   * 只在组件创建时调用一次,适合注册事件监听器
   */
  aboutToAppear(): void {
    console.info('MainPage aboutToAppear - 注册事件监听器')
    this.registerEventListeners()

    // 初始连接
    try {
      this.status = testNapi.init_connect(this.ip, Number(this.port))
      this.rightTopIconList[0].isSelect = this.status === 1
      console.info('初始连接状态:', this.status)
    } catch (error) {
      console.error('初始连接异常:', error)
      this.status = -1
      this.rightTopIconList[0].isSelect = false
    }
  }

  /**
   * 组件即将消失 - 生命周期函数
   * 清理资源,移除事件监听器
   */
  aboutToDisappear(): void {
    console.info('MainPage aboutToDisappear - 清理资源')
    this.clearAllTimers()
    this.unregisterEventListeners()

    // 断开连接(如果有相关API)
    try {
      // testNapi.disconnect()
    } catch (error) {
      console.error('断开连接异常:', error)
    }
  }

  /**
   * 页面显示时调用
   * 适合启动定时器和刷新UI
   */
  onPageShow(): void {
    console.info('MainPage onPageShow - 启动定时器')

    // 清除所有旧的定时器(防止重复)
    this.clearAllTimers()

    // 启动时间更新定时器(每1秒)
    this.timeID = setInterval(() => {
      try {
        this.updateTimeDate()
      } catch (error) {
        console.error('更新时间异常:', error)
      }
    }, 1000)

    // 启动心跳定时器(每5秒)
    // 心跳包同时负责:1.保持连接活跃 2.检测连接状态 3.断线重连
    this.heartbeatTimerID = setInterval(() => {
      try {
        this.sendHeartbeat()
      } catch (error) {
        console.error('心跳定时器执行异常:', error)
        // 发生异常,标记为未连接
        this.rightTopIconList[0].isSelect = false
      }
    }, 5000)

    // 页面显示时立即发送一次心跳,快速检查连接状态
    try {
      this.sendHeartbeat()
    } catch (error) {
      console.error('立即心跳检查异常:', error)
      this.rightTopIconList[0].isSelect = false
    }
  }

  /**
   * 页面隐藏时调用
   * 清理定时器以节省资源
   */
  onPageHide(): void {
    console.info('MainPage onPageHide - 清除定时器')
    this.clearAllTimers()
  }

  /**
   * 注册事件监听器
   * 只在组件创建时调用一次
   */
  private registerEventListeners(): void {
    const eventHub = getContext().eventHub

    // 监听IP和Port配置更新
    eventHub.on('传ip和port参数值', this.handleIpPortUpdate)

    console.info('事件监听器注册完成')
  }

  /**
   * 移除事件监听器
   * 在组件销毁时调用
   */
  private unregisterEventListeners(): void {
    const eventHub = getContext().eventHub

    // 移除IP和Port配置更新监听
    eventHub.off('传ip和port参数值', this.handleIpPortUpdate)

    console.info('事件监听器已移除')
  }

  /**
   * 清除所有定时器
   */
  private clearAllTimers(): void {
    if (this.timeID !== -1) {
      clearInterval(this.timeID)
      this.timeID = -1
      console.info('时间更新定时器已清除')
    }

    if (this.heartbeatTimerID !== -1) {
      clearInterval(this.heartbeatTimerID)
      this.heartbeatTimerID = -1
      console.info('心跳定时器已清除')
    }
  }

  /**
   * 发送心跳包
   * 功能:1.保持连接活跃 2.检测连接状态 3.自动重连
   */
  private sendHeartbeat(): void {
    // 检查当前连接状态
    if (!this.rightTopIconList[0].isSelect) {
      console.info('设备未连接,尝试重新连接')

      // 尝试重新连接
      try {
        this.status = testNapi.init_connect(this.ip, Number(this.port))

        if (this.status === 1) {
          this.rightTopIconList[0].isSelect = true
          console.info('重新连接成功', `ip:${this.ip}`, `port:${this.port}`)
          // 连接成功后,继续发送心跳包
        } else {
          this.rightTopIconList[0].isSelect = false
          console.error('重新连接失败', `status:${this.status}`)
          return  // 连接失败,不发送心跳包
        }
      } catch (error) {
        console.error('重新连接异常:', error)
        this.rightTopIconList[0].isSelect = false
        return
      }
    }

    // 已连接,发送心跳包到M7000寄存器
    // 计算M继电器的Modbus线圈地址 = 0x0800 + 继电器编号
    const coilAddr = 0x0800 + this.HEARTBEAT_REGISTER

    console.info(`发送心跳包到M${this.HEARTBEAT_REGISTER}寄存器(线圈地址:0x${coilAddr.toString(16)}),值为${this.HEARTBEAT_VALUE}`)

    try {
      this.heartbeatResult = testNapi.send_heartbeat(coilAddr, this.HEARTBEAT_VALUE)

      if (this.heartbeatResult === 1) {
        console.info('心跳包发送成功')
        // 心跳包发送成功,确保连接状态为已连接
        this.rightTopIconList[0].isSelect = true
      } else {
        console.error('心跳包发送失败,返回值:', this.heartbeatResult)
        // 心跳包发送失败,标记为未连接(下次心跳时会尝试重连)
        this.rightTopIconList[0].isSelect = false
      }
    } catch (error) {
      console.error('发送心跳包时发生异常:', error)
      // 发生异常,标记为未连接
      this.rightTopIconList[0].isSelect = false
    }
  }

  /**
   * 更新时间日期显示
   * 由定时器每秒调用
   */
  private updateTimeDate(): void {
    // 你的时间更新逻辑
    // 例如:this.currentTime = new Date().toLocaleTimeString()
  }

  /**
   * 手动触发连接
   * 可以由UI按钮调用
   */
  public manualConnect(): void {
    console.info('手动触发连接')

    try {
      this.status = testNapi.init_connect(this.ip, Number(this.port))

      if (this.status === 1) {
        this.rightTopIconList[0].isSelect = true
        console.info('手动连接成功')
      } else {
        this.rightTopIconList[0].isSelect = false
        console.error('手动连接失败', `status:${this.status}`)
      }
    } catch (error) {
      console.error('手动连接异常:', error)
      this.status = -1
      this.rightTopIconList[0].isSelect = false
    }
  }

  /**
   * 手动断开连接
   * 可以由UI按钮调用
   */
  public manualDisconnect(): void {
    console.info('手动断开连接')

    try {
      // 如果有断开连接的API
      // testNapi.disconnect()

      this.status = -1
      this.rightTopIconList[0].isSelect = false
      console.info('手动断开成功')
    } catch (error) {
      console.error('手动断开异常:', error)
    }
  }
}

在鸿蒙Next中,桥接C语言使用Modbus控制可通过鸿蒙NDK调用C库实现。鸿蒙提供Native API支持C/C++代码集成,允许直接使用Modbus的C语言库进行串口或TCP通信。开发者需在Native层封装Modbus协议栈,通过HarmonyOS的驱动框架操作硬件接口。注意鸿蒙Next的HDF驱动模型可管理设备资源,确保Modbus主从设备通信稳定。具体实现涉及创建Native工程、导入Modbus库、编写桥接代码及调用鸿蒙Native接口进行数据交互。

在HarmonyOS Next中通过Node-API桥接C/C++实现Modbus控制

核心是理解桥接层的作用和调用链路。从你的代码看,问题主要出在桥接层没有正确调用底层Modbus函数。

桥接文件调用顺序

  1. index.d.ts - TypeScript类型声明,定义ArkTS可调用的接口
  2. bridgeModbus.cpp - Node-API桥接层,将ArkTS调用转发到C函数
  3. napi_init.cpp - 模块初始化,注册函数到Node-API
  4. main.c - 实际的Modbus TCP实现

问题分析

从代码注释看,bridgeModbus.cpp中的init_connect函数只是临时返回1,没有实际调用modbusInitConnect。同样,send_heartbeat函数可能也存在类似问题。

解决方案

需要修改bridgeModbus.cpp,确保桥接函数正确调用底层C函数:

// 在bridgeModbus.cpp中
napi_value InitConnect(napi_env env, napi_callback_info info) {
    // 解析参数
    size_t argc = 2;
    napi_value args[2];
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    // 获取IP和端口
    char ip[16];
    size_t ip_len;
    napi_get_value_string_utf8(env, args[0], ip, sizeof(ip), &ip_len);
    
    int32_t port;
    napi_get_value_int32(env, args[1], &port);
    
    // 实际调用底层Modbus函数
    int result = modbusInitConnect(ip, port);
    
    // 返回结果给ArkTS
    napi_value napi_result;
    napi_create_int32(env, result, &napi_result);
    return napi_result;
}

关键检查点

  1. 确保CMakeLists.txt正确链接所有源文件
  2. 验证libmodbus库是否正确引入和编译
  3. 检查网络权限配置
  4. 确认M7000寄存器对应的Modbus线圈地址计算正确

桥接层的核心作用就是参数转换和函数转发,需要确保每个桥接函数都正确调用了对应的底层C函数实现。

回到顶部