当前位置: 首页 > news >正文

基于 OpenHarmony 6.0 的智能充电桩技术方案与实现

1. 引言与概述

本项目旨在基于 OpenHarmony 操作系统,开发一款符合行业标准的智能交流/直流充电桩。核心技术挑战在于实现与后台管理系统的稳定通信(OCPP)、高精度的电能计量与校准,以及在复杂网络环境下的可靠数据同步。
本文档将围绕以下三个核心模块展开,详细阐述其设计思路、技术选型、具体实现及模块间的整合方案:

  1. OCPP 1.6/2.0.1 通信协议栈:实现充电桩与中央管理系统(CMS)的实时双向通信。
  2. 电能计量与校准模块:确保充电电量数据的精确性和可追溯性。
  3. 车-桩-云协同与数据同步:保障充电记录在弱网或断网情况下的完整性与最终一致性。

2. 系统架构

系统采用分层架构设计,从上至下分为:应用层、框架与服务层、以及驱动与硬件层。
在这里插入图片描述

架构说明:

  • 应用层:使用 ArkTS 编写业务逻辑,包括 OCPP 状态机、计量服务、数据同步服务和用户界面。
  • 框架与服务层:利用 OpenHarmony 提供的系统能力,如网络通信(WebSocket)、安全存储(Huks)、分布式数据库等。
  • 驱动与硬件层:通过自定义 NAPI 模块桥接 ArkTS 与 HDF 驱动框架,与底层硬件(如 ADC、电表芯片)交互。

3. 核心功能实现

3.1 OCPP 1.6/2.0.1 通信协议实现
3.1.1 设计思路

OCPP(Open Charge Point Protocol)是基于 JSON 的 WebSocket 协议。我们在 ArkTS 层构建一个状态机来管理连接和消息流,并将充电会话与 Ability 生命周期关联。

3.1.2 技术选型
  • 语言/框架:ArkTS
  • 网络通信@kit.NetworkKit (WebSocket)
3.1.3 代码实现

1. OCPP 状态机管理器 (model/OcppManager.ets)

// model/OcppManager.ets
import { webSocket } from '@kit.NetworkKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
enum OcppState {DISCONNECTED = 'Disconnected',CONNECTING = 'Connecting',CONNECTED = 'Connected',REGISTERED = 'Registered',
}
const OCPP_MESSAGE_TYPE = {CALL: 2,CALL_RESULT: 3,CALL_ERROR: 4,
};
export class OcppManager {private ws: webSocket.WebSocket | null = null;private state: OcppState = OcppState.DISCONNECTED;private serverUrl: string = 'ws://your.cms.server/ocpp/';private chargePointId: string = 'CHARGE_POINT_001';private messageId: number = 1;constructor() {this.connect();}private connect(): void {if (this.state !== OcppState.DISCONNECTED) return;this.setState(OcppState.CONNECTING);this.ws = webSocket.createWebSocket();this.ws.on('open', (err: BusinessError) => {if (err) {hilog.error(0x0000, 'OcppManager', `WebSocket open error: ${JSON.stringify(err)}`);this.setState(OcppState.DISCONNECTED);return;}hilog.info(0x0000, 'OcppManager', 'WebSocket connected.');this.setState(OcppState.CONNECTED);this.sendBootNotification();});this.ws.on('message', (err: BusinessError, value: string | ArrayBuffer) => {if (err) return;this.handleIncomingMessage(value as string);});this.ws.on('close', () => {hilog.info(0x0000, 'OcppManager', 'WebSocket closed.');this.setState(OcppState.DISCONNECTED);// Implement reconnection logic here});this.ws.on('error', (err: BusinessError) => {hilog.error(0x0000, 'OcppManager', `WebSocket error: ${JSON.stringify(err)}`);this.setState(OcppState.DISCONNECTED);});this.ws.connect(this.serverUrl);}private setState(newState: OcppState): void {hilog.info(0x0000, 'OcppManager', `State transition: ${this.state} -> ${newState}`);this.state = newState;}private sendBootNotification(): void {const payload = { chargePointModel: "OH Charger", chargePointVendor: "MyCompany" };this.send('BootNotification', payload);}public send(action: string, payload: Object): void {if (this.state !== OcppState.CONNECTED && this.state !== OcppState.REGISTERED) return;const message = [OCPP_MESSAGE_TYPE.CALL, this.messageId++, action, payload];this.ws?.send(JSON.stringify(message));}private handleIncomingMessage(message: string): void {const [messageType, messageId, , payload] = JSON.parse(message);if (messageType === OCPP_MESSAGE_TYPE.CALL_RESULT && messageId === 1) {if (payload.status === 'Accepted') {this.setState(OcppState.REGISTERED);hilog.info(0x0000, 'OcppManager', 'Charge point registered.');}}}public startTransaction(connectorId: number, idTag: string): void {const payload = { connectorId, idTag, meterStart: 0, timestamp: new Date().toISOString() };this.send('StartTransaction', payload);}public stopTransaction(transactionId: number, idTag: string): void {const payload = { transactionId, idTag, meterStop: 1000, timestamp: new Date().toISOString() };this.send('StopTransaction', payload);}public getState(): OcppState {return this.state;}
}
3.1.4 讲解
  • 状态机OcppState 枚举管理连接生命周期,setState 方法封装状态切换。
  • WebSocket 封装:使用 @kit.NetworkKit 的 WebSocket API,监听核心事件,实现异步通信。
  • 消息格式:严格遵循 OCPP-J [messageType, messageId, action, payload] 格式。
  • 生命周期映射startTransactionstopTransaction 方法可供 UI Ability 在其生命周期回调中调用,实现充电会话与用户交互的关联。

3.2 电能计量校准
3.2.1 设计思路

ArkTS 应用无法直接访问硬件。我们通过 NAPI 模块作为桥梁,连接 ArkTS 与 C++ 编写的 HDF 驱动代码,实现 ADC 数据读取。校准系数使用 Huks 加密后,通过文件系统存储。

3.2.2 技术选型
  • 硬件交互:HDF ADC Driver + 自定义 NAPI 模块
  • 安全存储@kit.UnifiedKeyStoreKit (Huks) + @ohos.file.fs
3.2.3 代码实现

第一步:编写 C++ NAPI 模块 (src/main/cpp/adc_reader.cpp)

// src/main/cpp/adc_reader.cpp
#include "napi/native_api.h"
#include <hdf_log.h>
#include <adc_if.h>
#define ADC_DEVICE_NUM 0
#define ADC_CHANNEL_NUM 1
static napi_value ReadAdc(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);int32_t deviceNum, channelNum;napi_get_value_int32(env, args[0], &deviceNum);napi_get_value_int32(env, args[1], &channelNum);DevHandle *handle = AdcOpen(deviceNum);if (handle == nullptr) {HDF_LOGE("AdcOpen failed");return nullptr;}uint32_t readData;int32_t ret = AdcRead(handle, channelNum, &readData);AdcClose(handle);if (ret != HDF_SUCCESS) {HDF_LOGE("AdcRead failed, ret: %d", ret);return nullptr;}HDF_LOGI("ADC Read Success: %u", readData);napi_value result;napi_create_uint32(env, readData, &result);return result;
}
static napi_value Init(napi_env env, napi_value exports) {napi_property_descriptor desc[] = {{ "readAdc", nullptr, ReadAdc, nullptr, nullptr, nullptr, napi_default, nullptr },};napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);return exports;
}
NAPI_MODULE(NAPI_GITHUB_MODULE_NAME, Init)

第二步:配置编译 (src/main/cpp/BUILD.gn)

# src/main/cpp/BUILD.gn
import("//build/ohos.gni")
ohos_shared_library("adc_reader") {sources = [ "adc_reader.cpp" ]include_dirs = ["//drivers/hdf_core/adapter/uhdf2/include","//drivers/hdf_core/framework/include/core","//drivers/hdf_core/framework/include/utils","//third_party/bounds_checking_function/include",]deps = ["//drivers/hdf_core/adapter/uhdf2:libhdf","//drivers/hdf_core/adapter/uhdf2/metadata:libhdf_meta",]external_deps = [ "hiviewdfx_hilog_native:libhilog" ]output_name = "adc_reader" # ArkTS 中 import 时使用subsystem_name = "charger"part_name = "charger_part"
}

第三步:ArkTS 层调用 (model/MeteringService.ets)

// model/MeteringService.ets
import { hilog } from '@kit.PerformanceAnalysisKit';
import { common } from '@kit.AbilityKit';
import { HuksHelper } from './HuksHelper';
import adcReader from 'libadc_reader.so'; // 导入 NAPI 模块
interface CalibrationFactors { offset: number; scale: number; }
export class MeteringService {private calibrationFactors: CalibrationFactors = { offset: 0, scale: 1.0 };private readonly CALIBRATION_KEY_ALIAS = 'metering_calibration_factors';private context: common.UIAbilityContext;constructor(context: common.UIAbilityContext) {this.context = context;this.init();}private async init(): Promise<void> {await this.loadCalibrationFactors();}private async loadCalibrationFactors(): Promise<void> {try {const encryptedData = await HuksHelper.getData(this.context, 'calibration.bin');if (encryptedData) {const decryptedData = await HuksHelper.decrypt(this.CALIBRATION_KEY_ALIAS, encryptedData);this.calibrationFactors = JSON.parse(decryptedData);hilog.info(0x0000, 'MeteringService', `Calibration loaded: ${JSON.stringify(this.calibrationFactors)}`);}} catch (error) {hilog.error(0x0000, 'MeteringService', `Failed to load calibration: ${JSON.stringify(error)}`);}}public async calibrateAndSave(standardValue: number): Promise<void> {const rawValue = await this.getRawValue(0, 1);const newScale = rawValue > 0 ? standardValue / rawValue : 1.0;this.calibrationFactors = { offset: 0, scale: newScale };hilog.info(0x0000, 'MeteringService', `New calibration: ${JSON.stringify(this.calibrationFactors)}`);try {const plainData = JSON.stringify(this.calibrationFactors);const encryptedData = await HuksHelper.encrypt(this.CALIBRATION_KEY_ALIAS, plainData);await HuksHelper.saveData(this.context, 'calibration.bin', encryptedData);} catch (error) {hilog.error(0x0000, 'MeteringService', `Failed to save calibration: ${JSON.stringify(error)}`);}}private async getRawValue(device: number, channel: number): Promise<number> {try {return adcReader.readAdc(device, channel);} catch (error) {hilog.error(0x0000, 'MeteringService', `NAPI read failed: ${JSON.stringify(error)}`);return 0;}}public async getCalibratedEnergy(): Promise<number> {const rawValue = await this.getRawValue(0, 1);return (rawValue - this.calibrationFactors.offset) * this.calibrationFactors.scale;}
}

第四步:Huks 辅助类 (model/HuksHelper.ets)

// model/HuksHelper.ets
import { huks } from '@kit.UnifiedKeyStoreKit';
import { HksKeyAlg, HksKeyPurpose, HksTag } from '@kit.UnifiedKeyStoreKit';
import { fs } from '@ohos.file.fs';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
export class HuksHelper {private static readonly OPTIONS: huks.HuksOptions = {properties: [{ tag: HksTag.HKS_TAG_ALGORITHM, value: HksKeyAlg.HKS_ALG_AES },{ tag: HksTag.HKS_TAG_PURPOSE, value: HksKeyPurpose.HKS_KEY_PURPOSE_ENCRYPT | HksKeyPurpose.HKS_KEY_PURPOSE_DECRYPT },{ tag: HksTag.HKS_TAG_KEY_SIZE, value: 256 },{ tag: HksTag.HKS_TAG_BLOCK_MODE, value: huks.HksKeyBlockMode.HKS_MODE_GCM }],inData: new Uint8Array()};public static async getKey(alias: string): Promise<boolean> {try { await huks.isKeyExist(alias, this.OPTIONS); return true; }catch {try { await huks.generateKey(alias, this.OPTIONS); return true; }catch (e) { hilog.error(0x0000, 'HuksHelper', `Gen key failed: ${JSON.stringify(e)}`); return false; }}}public static async encrypt(alias: string, plainText: string): Promise<Uint8Array> {if (!(await this.getKey(alias))) throw new Error("Key not available");const options = { ...this.OPTIONS, inData: new Uint8Array(buffer.from(plainText, 'utf8')) };const result = await huks.encrypt(alias, options);return result.outData;}public static async decrypt(alias: string, cipherText: Uint8Array): Promise<string> {if (!(await this.getKey(alias))) throw new Error("Key not available");const options = { ...this.OPTIONS, inData: cipherText };const result = await huks.decrypt(alias, options);return String.fromCharCode.apply(null, result.outData);}public static async saveData(context: common.UIAbilityContext, fileName: string, data: Uint8Array): Promise<void> {const filePath = context.filesDir + '/' + fileName;const file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);fs.writeSync(file.fd, data);fs.closeSync(file);}public static async getData(context: common.UIAbilityContext, fileName: string): Promise<Uint8Array | null> {try {const filePath = context.filesDir + '/' + fileName;const file = fs.openSync(filePath, fs.OpenMode.READ_ONLY);const stat = fs.statSync(filePath);const buffer = new ArrayBuffer(stat.size);fs.readSync(file.fd, buffer);fs.closeSync(file);return new Uint8Array(buffer);} catch (error) {if ((error as BusinessError).code === 13900002) return null; // File not foundhilog.error(0x0000, 'HuksHelper', `Read file error: ${JSON.stringify(error)}`);return null;}}
}
3.2.4 讲解
  • NAPI 桥梁adc_reader.cpp 使用 HDF 的 C 接口读取 ADC,并通过 NAPI 将其暴露给 ArkTS。BUILD.gn 是编译此 C++ 代码为动态库的关键。
  • ArkTS 调用:通过 import adcReader from 'libadc_reader.so' 加载模块,并像调用普通 JS 函数一样调用 adcReader.readAdc()
  • 安全存储HuksHelper 完整实现了加密数据到文件的存取流程,MeteringService 依赖它来安全地管理校准系数。

3.3 车-桩-云协同与数据同步
3.3.1 设计思路

所有充电记录优先存入本地分布式数据库(KvStore),并加入一个“待同步队列”。通过监听网络状态,在网络恢复时自动将队列中的数据续传至云端,实现弱网补偿。

3.3.2 技术选型
  • 本地数据库@kit.ArkData (distributedKVStore)
  • 网络状态监听@kit.NetworkKit
3.3.3 代码实现
// model/DataSyncManager.ets
import { distributedKVStore } from '@kit.ArkData';
import { connection } from '@kit.NetworkKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { common } from '@kit.AbilityKit';
import { OcppManager } from './OcppManager';
import { OcppState } from './OcppManager'; // Import the enum
interface ChargingRecord {sessionId: string;startTime: string;endTime?: string;energy: number;synced: boolean;
}
export class DataSyncManager {private kvStore: distributedKVStore.SingleKVStore | null = null;private netConnection: connection.NetConnection | null = null;private readonly SYNC_QUEUE_KEY = 'sync_queue';private readonly STORE_ID = 'charging_records_db';private ocppManager: OcppManager;private context: common.UIAbilityContext;constructor(context: common.UIAbilityContext, ocppManager: OcppManager) {this.context = context;this.ocppManager = ocppManager;this.init();}private async init(): Promise<void> {await this.initKVStore();this.setupNetworkListener();}private async initKVStore(): Promise<void> {try {const kvStoreConfig: distributedKVStore.KVStoreConfig = { storeId: this.STORE_ID, context: this.context };const kvStoreManager = distributedKVStore.createKVStoreManager(this.context);this.kvStore = await kvStoreManager.getKVStore(kvStoreConfig);hilog.info(0x0000, 'DataSyncManager', 'KVStore initialized.');} catch (error) {hilog.error(0x0000, 'DataSyncManager', `KVStore init failed: ${JSON.stringify(error)}`);}}private setupNetworkListener(): void {this.netConnection = connection.createNetConnection();this.netConnection.on('netAvailable', () => {hilog.info(0x0000, 'DataSyncManager', 'Network available. Starting sync.');this.syncToCloud();});this.netConnection.register();}public async saveRecord(record: ChargingRecord): Promise<void> {if (!this.kvStore) return;try {await this.kvStore.put(record.sessionId, JSON.stringify(record));const queueData = await this.kvStore.get(this.SYNC_QUEUE_KEY, '[]');const queue: string[] = JSON.parse(queueData as string);if (!queue.includes(record.sessionId)) {queue.push(record.sessionId);await this.kvStore.put(this.SYNC_QUEUE_KEY, JSON.stringify(queue));}hilog.info(0x0000, 'DataSyncManager', `Record ${record.sessionId} saved.`);} catch (error) {hilog.error(0x0000, 'DataSyncManager', `Save record failed: ${JSON.stringify(error)}`);}}public async syncToCloud(): Promise<void> {if (!this.kvStore || this.ocppManager.getState() !== OcppState.REGISTERED) return;try {const queueData = await this.kvStore.get(this.SYNC_QUEUE_KEY, '[]');let queue: string[] = JSON.parse(queueData as string);if (queue.length === 0) return;hilog.info(0x0000, 'DataSyncManager', `Syncing ${queue.length} records...`);const failedQueue: string[] = [];for (const sessionId of queue) {const recordData = await this.kvStore.get(sessionId);if (recordData) {const record: ChargingRecord = JSON.parse(recordData as string);try {// Assume OcppManager has a method to send data// this.ocppManager.sendMeterValues(record); hilog.info(0x0000, 'DataSyncManager', `Successfully synced record ${sessionId}`);record.synced = true;await this.kvStore.put(sessionId, JSON.stringify(record));} catch (sendError) {hilog.error(0x0000, 'DataSyncManager', `Sync failed for ${sessionId}: ${JSON.stringify(sendError)}`);failedQueue.push(sessionId);}}}await this.kvStore.put(this.SYNC_QUEUE_KEY, JSON.stringify(failedQueue));} catch (error) {hilog.error(0x0000, 'DataSyncManager', `Sync process error: ${JSON.stringify(error)}`);}}
}
3.3.4 讲解
  • 依赖注入:构造函数接收 contextocppManager,降低了模块间的耦合度。
  • 本地优先saveRecord 总是先写入本地数据库,并更新同步队列,确保数据不丢失。
  • 自动同步setupNetworkListener 监听网络状态,一旦网络可用,自动触发 syncToCloud

4. 模块整合与启动流程

entry/src/main/ets/entryability/EntryAbility.ets

// entry/src/main/ets/entryability/EntryAbility.ets
import { UIAbility, AbilityConstant, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { OcppManager } from '../model/OcppManager';
import { MeteringService } from '../model/MeteringService';
import { DataSyncManager } from '../model/DataSyncManager';
// 全局服务实例,便于其他模块访问
export let globalOcppManager: OcppManager | null = null;
export let globalMeteringService: MeteringService | null = null;
export let globalDataSyncManager: DataSyncManager | null = null;
export default class EntryAbility extends UIAbility {onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {hilog.info(0x0000, 'EntryAbility', 'Ability onCreate');// 1. 初始化核心服务// OCPP Manager 不依赖其他服务,最先创建globalOcppManager = new OcppManager();// Metering Service 依赖 ContextglobalMeteringService = new MeteringService(this.context);// Data Sync Manager 依赖 Context 和 Ocpp ManagerglobalDataSyncManager = new DataSyncManager(this.context, globalOcppManager);hilog.info(0x0000, 'EntryAbility', 'All services initialized.');}onDestroy(): void {hilog.info(0x0000, 'EntryAbility', 'Ability onDestroy');// 可以在这里执行资源清理}onWindowStageCreate(windowStage: window.WindowStage): void {hilog.info(0x0000, 'EntryAbility', 'Ability onWindowStageCreate');windowStage.loadContent('pages/Index', (err, data) => {if (err.code) {hilog.error(0x0000, 'EntryAbility', 'Failed to load content. Cause: %{public}s', JSON.stringify(err) ?? '');return;}});}
}

整合讲解:

  1. 服务容器EntryAbility 作为应用入口,充当了简单的服务容器。它负责按依赖顺序创建所有核心服务实例。
  2. 依赖管理
    • OcppManager 是独立的,首先创建。
    • MeteringService 需要 context 来访问文件系统,因此传入 this.context
    • DataSyncManager 需要 contextOcppManager 实例,因此将两者都传入。
  3. 全局访问:通过导出全局变量,使得应用内的其他页面或组件可以方便地访问这些服务实例(例如,UI 页面可以调用 globalOcppManager.startTransaction())。在更复杂的应用中,可以考虑使用更高级的依赖注入框架。

待做:

  • UI/UX 完善:开发丰富的用户交互界面,如扫码充电、支付、账单展示等。
  • OCPP 2.0.1 深度集成:实现 ISO 15118 插充通信、智能充电等高级特性。
  • 分布式能力扩展:探索与手机、智能家居等设备的分布式协同,打造智慧充电生态。
http://www.dtcms.com/a/507104.html

相关文章:

  • 三步破局:一致性轨迹强化学习开启扩散语言模型“又快又好”推理新时代
  • Node.js | pnpm下载安装与环境配置
  • 递归-二叉树中的深搜-2331.计算布尔二叉树的值-力扣(LeetCode)
  • 下部刚刚是上部
  • 自动化产线效率低,主要看这四个环节
  • 如何查询网站开发语言杭州企业网站制作
  • sql server网站建设电子商务网络营销的概念
  • 网页制作基础教程代码网站seo软件
  • kafka中server.properties中的关键配置
  • 帧率、分辨率、码率
  • Linux补充01:HTTPS协议原理
  • 2025全球风电盛会CWP今日开展
  • Linux网络 网络层
  • 一个专门做各种恐怖片的电影网站怎样用记事本做网站
  • 织梦网站后台密码wordpress forandroid
  • STP的配置
  • 解锁细胞青春密码:美国 WJCZ 麦角硫因时光胶囊,用前沿生物科技对抗肌肤衰老
  • CTFSHOW—WEB4
  • MySQL InnoDB 状态(SHOW ENGINE INNODB STATUS)深度分析与性能优化建议
  • 全感知智慧校园场景大联动解决方案PPT(53页)
  • 分享一个成品的grafana表
  • sward V2.1.1版本发布,支持在线安装与消息配置等功能
  • 机器学习基础入门(第六篇):深度学习的兴起与神经网络基础
  • 京东联盟新手没有网站怎么做推广博物馆展陈设计公司
  • 【数据结构】最长的最短路径的求解
  • 网站后台管理产品排序网站被黑是怎么回事
  • jinji2模板
  • Linux route
  • 接10月12日---队列笔记
  • 第四章 串、数组和广义表——课后习题解练【数据结构(c语言版 第2版)】