Integração do SDK de Fechadura de Hotel V10 com HarmonyOS

Restrições Principais e Estratégia de Adaptação

O código original em C# depende de DLLs nativas do Windows (como proRFLV102024.dll) para interação com hardware (leitura de cartões, emissão, campainha), enquanto o HarmonyOS (especialmente em dispositivos) não pode chamar diretamente DLLs do Windows. A interação com hardware no HarmonyOS deve ser realizada através de APIs de acesso a hardware do HarmonyOS (como USB, drivers de periféricos) ou de um SDK adaptado para HarmonyOS fornecido pelo fabricante.

A estratégia de conversão é dividida em duas partes:

  1. Preservar a lógica de negócio principal (análise de protocolo, lógica de emissão/cancelamento de cartões, registro de logs) implementando-a em ArkTS (a principal linguagem de desenvolvimento do HarmonyOS);
  2. Para a interação com hardware, fornecer um esquema de adaptação de acesso a hardware para dispositivos HarmonyOS (marcando onde a lógica de chamada da DLL original deve ser substituída).

Preparação do Ambiente de Desenvolvimento HarmonyOS

  1. Instale o DevEco Studio (IDE oficial do HarmonyOS);
  2. Crie um projeto HarmonyOS (selecione o modelo Stage, baseado em ArkTS);
  3. Se envolver interação com hardware, configure as permissões de hardware do dispositivo (como permissões de USB, acesso a periféricos).

Exemplos de Código em ArkTS para Dispositivos/Servidores HarmonyOS

O código abaixo é dividido em classes de lógica de negócio principal e entrada de UI/serviço HarmonyOS, mantendo a lógica central de emissão de cartões para check-in, cancelamento para check-out e leitura de cartões, adaptando-se à sintaxe e APIs do HarmonyOS.

1. Classe Utilitária: Análise de Protocolo e Registro de Logs (ProtocolUtils.ets)


/**
 * Classe para análise de protocolos, similar à classe original CyberWinAPPProtocolPackage
 * Adaptada para o HarmonyOS
 */
export class ProtocolAnalyzer {
    private parsedData: Map<string, string> = new Map();

    /**
     * Analisa uma string de parâmetros (lógica simulada, expansível conforme o protocolo real)
     * @param params String de parâmetros, formato como "hotelsign=123&lockno=456789"
     */
    parse(params: string): void {
        // Usar URLSearchParams para simplificar a análise no HarmonyOS
        const searchParams = new URLSearchParams(params);
        searchParams.forEach((value, key) => {
            this.parsedData.set(key, value);
        });
    }

    /**
     * Obtém um parâmetro analisado
     * @param key Nome do parâmetro
     * @returns Valor do parâmetro (string vazia se inexistente)
     */
    getValue(key: string): string {
        return this.parsedData.get(key) || '';
    }
}

/**
 * Classe utilitária para logs, adaptada à API de sistema de arquivos do HarmonyOS
 */
export class LoggerService {
    /**
     * Escreve uma entrada de log (equivalente ao método write_log original)
     * @param category Categoria do log
     * @param logType Tipo do log
     * @param message Conteúdo do log
     */
    public static async recordLog(category: string, logType: string, message: string): Promise<void> {
        try {
            // Obter caminho do sandbox do aplicativo no HarmonyOS (requer permissão de arquivo: ohos.permission.WRITE_USER_STORAGE)
            const appContext = getContext(this);
            const logDirectory = `${appContext.filesDir}/logs/${category}/${new Date().toISOString().split('T')[0]}`;
            // Criar diretório usando a API fileio do HarmonyOS
            const fileio = require('@ohos.fileio');
            if (!fileio.accessSync(logDirectory)) {
                fileio.mkdirSync(logDirectory, { recursive: true });
            }
            // Caminho do arquivo de log
            const logFilePath = `${logDirectory}/${logType}.log`;
            // Escrever log (modo de anexação)
            const logEntry = `=== Registro ===\n${new Date().toLocaleString()} - INÍCIO\n${message}\n\n`;
            fileio.writeFileSync(logFilePath, logEntry, { flag: 'a', encoding: 'utf-8' });
        } catch (error) {
            console.error(`Falha ao escrever log: ${error}`);
        }
    }
}

/**
 * Classe para análise de dados do cartão, similar à CyberWin_LocakAPP original
 */
export class CartaoDataParser {
    /**
     * Analisa o identificador do hotel a partir dos dados do cartão
     * @param cardBuffer Dados do cartão em Uint8Array
     * @returns Identificador do hotel ou mensagem de aviso
     */
    public static extractHotelId(cardBuffer: Uint8Array): string {
        // Converter para string ASCII (equivalente a Encoding.ASCII.GetString original)
        const cardString = new TextDecoder('ascii').decode(cardBuffer);
        // Verificar se é um cartão em branco
        if (this.extractSubstring(cardBuffer, 25, 8) === 'FFFFFFFF') {
            console.log('Este cartão está em branco, use um cartão que abra a porta');
            return 'Cartão em branco';
        }
        // Analisar identificador do hotel (lógica original mantida)
        const segment1 = this.extractSubstring(cardBuffer, 11, 4);
        let value1 = parseInt(segment1, 16) % 16384;
        const segment2 = this.extractSubstring(cardBuffer, 9, 2);
        value1 = value1 + (parseInt(segment2, 16) * 65536);
        const finalValue = parseInt(this.extractSubstring(cardBuffer, 9, 2), 16) * 65536 + parseInt(this.extractSubstring(cardBuffer, 11, 4), 16) % 16383;
        return finalValue.toString();
    }

    /**
     * Extrai uma substring de um buffer de dados (equivalente ao método Copy original)
     * @param buffer Buffer de dados
     * @param startPosition Posição inicial (começando em 1)
     * @param length Comprimento da substring
     * @returns Substring extraída
     */
    private static extractSubstring(buffer: Uint8Array, startPosition: number, length: number): string {
        const bufferString = new TextDecoder('ascii').decode(buffer);
        const adjustedStart = startPosition < 1 ? 1 : startPosition;
        return bufferString.substring(adjustedStart - 1, adjustedStart - 1 + length);
    }
}

2. Classe de Negócio Princiapl: Controle de Fechadura de Hotel (LockManager.ets)


import { ProtocolAnalyzer, LoggerService, CartaoDataParser } from './ProtocolUtils';

/**
 * Classe de negócio principal para fechadura de hotel (equivalente à classe APP original)
 * No HarmonyOS, pode-se usar o padrão Singleton (opcional)
 */
export class LockManager {
    private static instance: LockManager;
    private cardDataBuffer: Uint8Array = new Uint8Array(128); // Equivalente a byte[128]
    private idPhotoPath: string = '';
    public static readBuffer: Uint8Array = new Uint8Array(128 + 1); // Buffer de leitura de cartão
    public static v10ReadBuffer: Uint8Array = new Uint8Array(200 + 1); // Buffer para leitura V10
    public static operationStatus: number = 0; // Código de status

    // Padrão Singleton (comum no HarmonyOS)
    public static getInstance(): LockManager {
        if (!this.instance) {
            this.instance = new LockManager();
        }
        return this.instance;
    }

    // -------------------------- Adaptação de Interação com Hardware (substituindo chamadas DLL) --------------------------
    /**
     * Inicializa o dispositivo USB (equivalente a initializeUSB original, precisa ser substituído pela API USB do HarmonyOS)
     * @param deviceType 0=USB com driver, 1=proUSB
     * @returns 0=sucesso, outro=falha
     */
    private initializeUsbDevice(deviceType: number): number {
        // 【Adaptação de Hardware HarmonyOS】Substituir pela lógica de inicialização de dispositivo USB do HarmonyOS
        // Referência: https://developer.harmonyos.com/cn/docs/documentation/doc-references/usb_device-0000001524415869
        // Exemplo: obter lista de dispositivos USB, abrir dispositivo específico
        try {
            // Simular retorno de sucesso; na prática, integrar com SDK de hardware
            return 0;
        } catch (e) {
            return -1;
        }
    }

    /**
     * Controle da campainha (equivalente a Buzzer original, precisa ser substituído por API de periféricos)
     * @param deviceHandle Identificador do dispositivo
     * @param duration Duração em ms
     * @returns 0=sucesso, outro=falha
     */
    private activateBuzzer(deviceHandle: number, duration: number): number {
        // 【Adaptação de Hardware HarmonyOS】Substituir pela lógica de controle de campainha (via API GPIO se suportado)
        console.log(`Campainha ativada por ${duration}ms`);
        return 0;
    }

    /**
     * Cancela um cartão (equivalente a CardErase original, precisa ser substituído por SDK de hardware)
     * @param deviceHandle Identificador do dispositivo
     * @param hotelId Identificador do hotel
     * @param cardNumber Número do cartão (buffer)
     * @returns 0=sucesso, outro=falha
     */
    private eraseCardData(deviceHandle: number, hotelId: number, cardNumber: string): number {
        // 【Adaptação de Hardware HarmonyOS】Substituir pela lógica de cancelamento de cartão (chamada ao SDK de hardware)
        try {
            // Simular retorno de sucesso
            return 0;
        } catch (e) {
            return -1;
        }
    }

    /**
     * Emite um cartão (equivalente a GuestCard_原始 original, precisa ser substituído por SDK de hardware)
     * @param issuanceParams Parâmetros de emissão
     * @returns 0=sucesso, outro=falha
     */
    private issueGuestCard(issuanceParams: {
        deviceHandle: number,
        hotelId: number,
        cardIndex: number,
        roomType: number,
        lockFlag: number,
        doorAccess: number,
        validFrom: string,
        validUntil: string,
        lockIdentifier: string,
        cardPayload: string
    }): number {
        // 【Adaptação de Hardware HarmonyOS】Substituir pela lógica de emissão de cartão (chamada ao SDK de hardware)
        try {
            // Simular retorno de sucesso
            return 0;
        } catch (e) {
            return -1;
        }
    }

    /**
     * Lê um cartão V10 (equivalente a rdCard_v10 original, precisa ser substituído por SDK de hardware)
     * @returns true=sucesso, false=falha
     */
    private performV10CardRead(): boolean {
        // 【Adaptação de Hardware HarmonyOS】Substituir pela lógica de leitura de cartão (chamada ao SDK de hardware)
        try {
            // Simular leitura bem-sucedida, preencher buffer
            LockManager.v10ReadBuffer = new Uint8Array(201).fill(0x30); // Dados simulados
            LockManager.operationStatus = 0;
            return true;
        } catch (e) {
            LockManager.operationStatus = -1;
            console.error(`Falha na leitura do cartão: ${e}`);
            return false;
        }
    }

    // -------------------------- Implementação dos Métodos de Negócio Originais --------------------------
    /**
     * Método de inicialização (equivalente a start)
     * @param params Conjunto de parâmetros
     * @returns String de resultado
     */
    initialize(params: Record<string, string>): string {
        const configValue = params['config'] || '';
        return 'Plugin pré-instalado aleatório';
    }

    /**
     * Verificação de status do dispositivo (equivalente a status)
     * @param params Conjunto de parâmetros
     * @returns String de resultado
     */
    checkDeviceStatus(params: Record<string, string>): string {
        this.activateBuzzer(1, 50); // Controlar campainha
        return 'Quando a campainha do dispositivo soar, indica que ele está conectado';
    }

    /**
     * Processo de check-out com cancelamento de cartão (equivalente a checkingout)
     * @param params Conjunto de parâmetros
     * @returns String de resultado
     */
    processCheckout(params: Record<string, string>): string {
        let outcome = 'Cancelamento de cartão';
        const queryParams = params['params'] || '';

        // Analisar parâmetros do protocolo
        const analyzer = new ProtocolAnalyzer();
        analyzer.parse(queryParams);
        const hotelSign = analyzer.getValue('hotelsign');
        if (!hotelSign) {
            return `${outcome}: Identificador do hotel vazio`;
        }

        // Inicializar dispositivo USB
        const usbInitStatus = this.initializeUsbDevice(1);
        if (usbInitStatus !== 0) {
            console.log('Falha ao abrir dispositivo');
            return 'Falha ao abrir porta';
        }

        // Chamar função de cancelamento de cartão
        const simulatedCardNo = '';
        const operationResult = this.eraseCardData(1, parseInt(hotelSign), simulatedCardNo);
        if (operationResult !== 0) {
            outcome += `: Falha no cancelamento ${operationResult}`;
        } else {
            outcome += ': Sucesso';
        }

        // Registrar log
        LoggerService.recordLog('hotel', 'checkout', outcome);

        return outcome;
    }

    /**
     * Processo de check-in com emissão de cartão (equivalente a checkingin)
     * @param params Conjunto de parâmetros
     * @returns String de resultado
     */
    processCheckin(params: Record<string, string>): string {
        let outcome = 'Emissão de cartão para check-in';
        const queryParams = params['params'] || '';

        // Analisar parâmetros do protocolo
        const analyzer = new ProtocolAnalyzer();
        analyzer.parse(queryParams);
        const lockNumber = analyzer.getValue('lockno');
        const hotelSign = analyzer.getValue('hotelsign');
        const checkoutTime = analyzer.getValue('checkingouttime');

        // Validar comprimento do número da fechadura
        if (lockNumber.length < 6) {
            LoggerService.recordLog('hotel', 'checkin', `Comprimento do número da fechadura inválido=${lockNumber}`);
            return `${outcome}: Comprimento do número da fechadura inválido`;
        }

        // Inicializar dispositivo USB
        const usbInitStatus = this.initializeUsbDevice(1);
        if (usbInitStatus !== 0) {
            console.log('Falha ao abrir dispositivo');
            return 'Falha ao abrir porta';
        }

        // Construir parâmetros de emissão
        const accessStartTime = new Date().toISOString().replace(/[^0-9]/g, '').substring(0, 14); // Formato: aaaammddHHmmss
        const lockFlagValue = 1; // Sinalizador de trancamento
        const roomTypeValue = 1; // Identificador do tipo de quarto

        // Chamar função de emissão de cartão
        const issuanceResult = this.issueGuestCard({
            deviceHandle: 1,
            hotelId: parseInt(hotelSign),
            cardIndex: 0,
            roomType: roomTypeValue,
            lockFlag: lockFlagValue,
            doorAccess: 0,
            validFrom: accessStartTime,
            validUntil: checkoutTime,
            lockIdentifier: lockNumber,
            cardPayload: ''
        });

        if (issuanceResult !== 0) {
            outcome += ' Falha na emissão do cartão';
        } else {
            outcome += ` Cartão emitido com sucesso V2024 ${lockNumber}`;
        }

        // Registrar log
        LoggerService.recordLog('hotel', 'checkin', outcome);

        return outcome;
    }

    /**
     * Lê o identificador do hotel (equivalente a getsign)
     * @param params Conjunto de parâmetros
     * @returns Identificador do hotel ou mensagem de aviso
     */
    retrieveHotelId(params: Record<string, string>): string {
        // Ler cartão
        if (!this.performV10CardRead()) {
            LoggerService.recordLog('hotel', 'getsign', 'Falha na leitura do cartão');
            return 'Falha na leitura do cartão';
        }

        // Analisar identificador do hotel
        const hotelId = CartaoDataParser.extractHotelId(LockManager.v10ReadBuffer);
        LoggerService.recordLog('hotel', 'getsign', `Identificador do hotel: ${hotelId}`);
        return hotelId;
    }
}

3. Página de Interface do Usuário HarmonyOS: Exemplo de Chamada (MainPage.ets)


import { LockManager } from './LockManager';

@Entry
@Component
struct HotelLockControlPage {
    private lockController = LockManager.getInstance();
    @State displayResult: string = '';

    build() {
        Column() {
            Text('Controle de Fechadura de Hotel')
                .fontSize(30)
                .fontWeight(FontWeight.Bold)
                .margin({ bottom: 20 });

            // Botão para verificar status do dispositivo
            Button('Verificar Status do Dispositivo')
                .onClick(() => {
                    this.displayResult = this.lockController.checkDeviceStatus({});
                })
                .margin({ bottom: 10 });

            // Botão para cancelamento no check-out
            Button('Cancelar Cartão no Check-out')
                .onClick(() => {
                    // Parâmetros simulados: hotelsign=123456
                    this.displayResult = this.lockController.processCheckout({ params: 'hotelsign=123456' });
                })
                .margin({ bottom: 10 });

            // Botão para emissão no check-in
            Button('Emitir Cartão no Check-in')
                .onClick(() => {
                    // Parâmetros simulados: hotelsign=123456&lockno=123456&checkingouttime=2501010000
                    this.displayResult = this.lockController.processCheckin({
                        params: 'hotelsign=123456&lockno=123456&checkingouttime=2501010000'
                    });
                })
                .margin({ bottom: 10 });

            // Botão para ler identificador do hotel
            Button('Ler Identificador do Hotel')
                .onClick(() => {
                    this.displayResult = this.lockController.retrieveHotelId({});
                })
                .margin({ bottom: 10 });

            // Exibição do resultado
            Text(this.displayResult)
                .fontSize(16)
                .margin({ top: 20 })
                .textAlign(TextAlign.Center);
        }
        .width('100%')
        .height('100%')
        .padding(20)
        .justifyContent(FlexAlign.Center);
    }
}

Pontos-Chave Explicados

  1. Adaptação de Interação com Hardware: As chamadas DLL originais em C# foram substituídas por uma camada de adaptação de APIs de hardware do HarmonyOS, indicando onde a lógica de inicialização USB, controle de campainha e operações de cartão deve ser implementada usando as APIs HarmonyOS ou SDKs de hardware de terceiros.
  2. Preservação da Lógica de Negócio: A análise de protocolo, validação de parâmetros, lógica de emissão/cancelamento e reigstro de logs foram mantidas integralmente, adaptadas apanas à sintaxe do ArkTS e às APIs do HarmonyOS (como fileio para logs, TextDecoder para codificação de caracteres).
  3. Adaptação a Características do HarmonyOS: Utilização do padrão Singleton, UI declarativa com decoradores @Entry e @Component, e separação clara entre lógica de negócios e interface.

Complemento sobre Interação com Hardware no HarmonyOS

Para implementar interação real com hardware (leitura/emissão de cartões) em dispositivos HarmonyOS, é necessário:

  1. Solicitar permissões de hardware: Configurar permissões como ohos.permission.USB_DEVICE no arquivo module.json5;
  2. Integrar com SDK de hardware: Contatar o fabricante para obter o SDK de fechadura de hotel compatível com HarmonyOS (substituindo a DLL original do Windows);
  3. Utilizar APIs de hardware do HarmonyOS: Consultar a documentação oficial do HarmonyOS sobre desenvolvimento de dispositivos USB e desenvolvimento de GPIO.

Tags: HarmonyOS ArkTS Hotel Door Lock SDK Integration USB Hardware

Publicado em 6-16 06:06 por Thomas