e1b203fd创建于 11 天前历史提交
/*
 * Copyright (c) 2026 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <iostream>
#include <string>
#include <thread>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>
#include <cstring>
#include <hdf_log.h>
#include "v1_0/iserials.h"
#include "v1_0/iserial_device_callback.h"

using namespace OHOS::HDI::Serial::V1_0;

constexpr int DEFAULT_BAUD_RATE = 115200;
constexpr int DEFAULT_DATA_BITS = 8;
constexpr int DEFAULT_STOP_BITS = 1;
constexpr int DEFAULT_PARITY = 0;
constexpr int DEFAULT_TIMEOUT = 1000;
constexpr int TEST_WAIT_DATA_TIMEOUT_MS = 1000;
constexpr int TEST_WAIT_OFFLINE_TIMEOUT_MS = 5000;
constexpr int TEST_READ_TIMEOUT_MS = 500;
constexpr int TEST_ECHO_READ_TIMEOUT_MS = 2000;
constexpr int TEST_PRINT_DATA_MAX_LEN = 64;
constexpr int TEST_SLEEP_TIME_MS = 100;
constexpr int TEST_ASCII_MIN = 32;
constexpr int TEST_ASCII_MAX = 126;
constexpr int MENU_CHOICE_MIN = 1;
constexpr int MENU_BAUD_RATE_MAX = 8;
constexpr int MENU_DATA_BITS_MAX = 4;
constexpr int MENU_STOP_BIT_TWO = 2;
constexpr int MENU_APPLY_CHOICE = 7;
constexpr int MENU_RESET_CHOICE = 8;
constexpr int MENU_EXIT_CHOICE = 0;

class SerialDeviceCallbackImpl : public ISerialDeviceCallback {
public:
    SerialDeviceCallbackImpl() : deviceOffline_(false) {}
    ~SerialDeviceCallbackImpl() override = default;

    int32_t OnDeviceOffline() override
    {
        deviceOffline_ = true;
        offlineCond_.notify_all();
        std::cout << "\n[Device offline detected]\n";
        return HDF_SUCCESS;
    }

    int32_t OnReadData(const std::vector<int8_t>& data, uint32_t dataLen) override
    {
        std::lock_guard<std::mutex> lock(dataMutex_);
        receivedData_.push({data, dataLen});
        dataCond_.notify_one();
        return HDF_SUCCESS;
    }

    bool WaitForData(std::vector<int8_t>& data, uint32_t& dataLen, int timeoutMs = TEST_WAIT_DATA_TIMEOUT_MS)
    {
        std::unique_lock<std::mutex> lock(dataMutex_);
        if (dataCond_.wait_for(lock, std::chrono::milliseconds(timeoutMs),
            [this] { return !receivedData_.empty(); })) {
            auto front = receivedData_.front();
            receivedData_.pop();
            data = front.first;
            dataLen = front.second;
            uint32_t len = 0;
            for (auto val : data) {
                std::cout << static_cast<int>(val) << " ";
                ++len;
                if (len >= dataLen) {
                    break;
                }
            }
            std::cout << std::endl;
            return true;
        }
        return false;
    }

    bool WaitForOffline(int timeoutMs = TEST_WAIT_OFFLINE_TIMEOUT_MS)
    {
        std::unique_lock<std::mutex> lock(offlineMutex_);
        return offlineCond_.wait_for(lock, std::chrono::milliseconds(timeoutMs),
            [this] { return deviceOffline_.load(); });
    }

    bool IsDeviceOffline() const { return deviceOffline_.load(); }

    void ClearBuffer()
    {
        std::lock_guard<std::mutex> lock(dataMutex_);
        while (!receivedData_.empty()) {
            receivedData_.pop();
        }
    }

    void Reset() { deviceOffline_.store(false); }

private:
    std::atomic<bool> deviceOffline_;
    std::mutex dataMutex_;
    std::mutex offlineMutex_;
    std::condition_variable dataCond_;
    std::condition_variable offlineCond_;
    std::queue<std::pair<std::vector<int8_t>, uint32_t>> receivedData_;
};

class SerialConfigMenu {
public:
    void DisplayMenu()
    {
        std::cout << "\n========== Serial Configuration Menu ==========\n";
        std::cout << "1. Baud Rate (current: " << config_.baudRate << ")\n";
        std::cout << "2. Data Bits (current: " << config_.dataBits << ")\n";
        std::cout << "3. Stop Bits (current: " << config_.stopBits << ")\n";
        std::cout << "4. Parity (current: " << GetParityString(config_.parity) << ")\n";
        std::cout << "5. RTS/CTS (current: " << (config_.rtscts ? "On" : "Off") << ")\n";
        std::cout << "6. XON/XOFF (current: " << (config_.xon ? "On" : "Off") << ")\n";
        std::cout << "7. Apply\n8. Reset\n0. Exit\n";
        std::cout << "================================================\n";
    }

    void HandleMenuChoice(int choice)
    {
        switch (choice) {
            case 1: ConfigureBaudRate(); break;
            case 2: ConfigureDataBits(); break;
            case 3: ConfigureStopBits(); break;
            case 4: ConfigureParity(); break;
            case 5: ConfigureRtsCts(); break;
            case 6: ConfigureXonXoff(); break;
            case 7: ApplyConfig(); break;
            case 8: ResetToDefaults(); break;
            default: break;
        }
    }

    SerialConfig GetConfig() const { return config_; }
    void SetConfig(const SerialConfig& config) { config_ = config; }

    void ConfigureBaudRate()
    {
        std::cout << "\nSelect Baud Rate:\n";
        std::cout << "1.9600 2.19200 3.38400 4.57600 5.115200 6.230400 7.460800 8.921600\n";
        int choice;
        std::cin >> choice;
        int rates[] = {9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600};
        config_.baudRate = (choice >= MENU_CHOICE_MIN && choice <= MENU_BAUD_RATE_MAX) ?
            rates[choice - MENU_CHOICE_MIN] : DEFAULT_BAUD_RATE;
        std::cout << "Baud rate set to: " << config_.baudRate << "\n";
    }

    void ConfigureDataBits()
    {
        std::cout << "\nSelect Data Bits: 1.5 2.6 3.7 4.8\n";
        int choice;
        std::cin >> choice;
        config_.dataBits = (choice >= MENU_CHOICE_MIN && choice <= MENU_DATA_BITS_MAX) ?
            choice + MENU_DATA_BITS_MAX : DEFAULT_DATA_BITS;
        std::cout << "Data bits set to: " << config_.dataBits << "\n";
    }

    void ConfigureStopBits()
    {
        std::cout << "\nSelect Stop Bits: 1.1 2.2\n";
        int choice;
        std::cin >> choice;
        config_.stopBits = (choice == MENU_STOP_BIT_TWO) ? MENU_STOP_BIT_TWO : DEFAULT_STOP_BITS;
        std::cout << "Stop bits set to: " << config_.stopBits << "\n";
    }

    void ConfigureParity()
    {
        std::cout << "\nSelect Parity: 0.None 1.Odd 2.Even\n";
        int choice;
        std::cin >> choice;
        config_.parity = (choice >= 0 && choice <= 2) ? choice : 0;
        std::cout << "Parity set to: " << GetParityString(config_.parity) << "\n";
    }

    void ConfigureRtsCts()
    {
        std::cout << "\nRTS/CTS: 0.Off 1.On\n";
        int choice;
        std::cin >> choice;
        config_.rtscts = (choice == 1);
        std::cout << "RTS/CTS: " << (config_.rtscts ? "On" : "Off") << "\n";
    }

    void ConfigureXonXoff()
    {
        std::cout << "\nXON/XOFF: 0.Off 1.XON 2.XOFF 3.XANY\n";
        int choice;
        std::cin >> choice;
        config_.xon = (choice == 1);
        config_.xoff = (choice == 2);
        config_.xany = (choice == 3);
    }

    void ApplyConfig() { std::cout << "Configuration applied.\n"; }

    void ResetToDefaults()
    {
        config_.baudRate = DEFAULT_BAUD_RATE;
        config_.dataBits = DEFAULT_DATA_BITS;
        config_.stopBits = DEFAULT_STOP_BITS;
        config_.parity = DEFAULT_PARITY;
        config_.rtscts = false;
        config_.xon = false;
        config_.xoff = false;
        config_.xany = false;
        std::cout << "Configuration reset to defaults.\n";
    }

    std::string GetParityString(int parity) const
    {
        const char* names[] = {"None", "Odd", "Even"};
        return (parity >= 0 && parity <= 2) ? names[parity] : "Unknown";
    }
private:
    SerialConfig config_ = {DEFAULT_BAUD_RATE, DEFAULT_DATA_BITS, DEFAULT_STOP_BITS,
        DEFAULT_PARITY, false, false, false, false};
};

class SerialSampleTest {
public:
    SerialSampleTest() : running_(false), readThreadRunning_(false) {}
    ~SerialSampleTest() { StopReadThread(); CloseDevice(); }

    bool Init()
    {
        serials_ = ISerials::Get(true);
        if (serials_ == nullptr) {
            std::cout << "Failed to get serial service.\n";
            return false;
        }
        std::cout << "Serial service initialized.\n";
        return true;
    }

    bool QueryDevices()
    {
        if (serials_ == nullptr) {
            std::cout << "Service not initialized.\n";
            return false;
        }

        std::vector<SerialDeviceInfo> devices;
        int32_t ret = serials_->QueryDevices(devices);
        if (ret != HDF_SUCCESS) {
            std::cout << "QueryDevices failed.\n";
            return false;
        }

        if (devices.empty()) {
            std::cout << "No devices found.\n";
            return true;
        }

        PrintDeviceList(devices);
        devices_ = devices;
        return true;
    }

    bool OpenDevice(int deviceIndex, const SerialConfig& config)
    {
        if (serials_ == nullptr || devices_.empty()) {
            std::cout << "No devices available.\n";
            return false;
        }

        if (deviceIndex < 0 || deviceIndex >= static_cast<int>(devices_.size())) {
            deviceIndex = 0;
        }

        if (device_ != nullptr) {
            std::cout << "Device already open.\n";
            return false;
        }

        callback_ = new SerialDeviceCallbackImpl();
        std::string portName = devices_[deviceIndex].portName;
        int32_t ret = serials_->OpenDevice(portName, config, callback_, device_);
        if (ret != HDF_SUCCESS) {
            std::cout << "OpenDevice failed.\n";
            callback_ = nullptr;
            return false;
        }

        std::cout << "Device opened: " << portName << "\n";
        return true;
    }

    void CloseDevice()
    {
        StopReadThread();
        if (device_ != nullptr) {
            device_->Close();
            device_ = nullptr;
            std::cout << "Device closed.\n";
        }
        callback_ = nullptr;
    }

    bool WriteData(const std::vector<uint8_t>& data)
    {
        if (device_ == nullptr) {
            std::cout << "Device not open.\n";
            return false;
        }

        int32_t bytesWritten = 0;
        int32_t ret = device_->Write(data, DEFAULT_TIMEOUT, bytesWritten);
        if (ret != HDF_SUCCESS) {
            std::cout << "Write failed.\n";
            return false;
        }
        std::cout << "Written " << bytesWritten << " bytes.\n";
        return true;
    }

    bool ReadData(std::vector<int8_t>& data, uint32_t& dataLen, int timeoutMs = 1000)
    {
        if (callback_ == nullptr) {
            return false;
        }
        return callback_->WaitForData(data, dataLen, timeoutMs);
    }

    void StartReadThread()
    {
        if (device_ == nullptr || readThreadRunning_) {
            return;
        }

        int32_t ret = device_->StartRead();
        if (ret != HDF_SUCCESS) {
            std::cout << "StartRead failed.\n";
            return;
        }

        readThreadRunning_ = true;
        running_ = true;
        readThread_ = std::thread(&SerialSampleTest::ReadThreadFunc, this);
        std::cout << "Read thread started.\n";
    }

    void StopReadThread()
    {
        if (!readThreadRunning_) {
            return;
        }

        running_ = false;
        if (device_ != nullptr) {
            device_->StopRead();
        }
        if (readThread_.joinable()) {
            readThread_.join();
        }
        readThreadRunning_ = false;
        std::cout << "Read thread stopped.\n";
    }

    bool Flush()
    {
        if (device_ == nullptr) {
            return false;
        }
        return device_->Flush() == HDF_SUCCESS;
    }

    bool Drain()
    {
        if (device_ == nullptr) {
            return false;
        }
        return device_->Drain() == HDF_SUCCESS;
    }

    bool SendBreak()
    {
        if (device_ == nullptr) {
            return false;
        }
        return device_->SendBrkSignal() == HDF_SUCCESS;
    }

    bool SetRts(bool rts)
    {
        if (device_ == nullptr) {
            return false;
        }
        return device_->SetRtsSignal(rts) == HDF_SUCCESS;
    }

    bool GetCts(bool& cts)
    {
        if (device_ == nullptr) {
            return false;
        }
        return device_->GetCtsSignal(cts) == HDF_SUCCESS;
    }

    bool IsDeviceOpen() const { return device_ != nullptr; }
    bool IsReadThreadRunning() const { return readThreadRunning_; }
    bool IsDeviceOffline() const { return callback_ != nullptr && callback_->IsDeviceOffline(); }

    void PrintDeviceList(const std::vector<SerialDeviceInfo>& devices)
    {
        std::cout << "\n========== Available Devices ==========\n";
        for (size_t i = 0; i < devices.size(); ++i) {
            std::cout << i << ": " << devices[i].portName << " (" << devices[i].manufacturer << ")\n";
        }
        std::cout << "========================================\n";
    }

    void ReadThreadFunc()
    {
        std::cout << "Read thread waiting for data...\n";
        while (running_ && !IsDeviceOffline()) {
            std::vector<int8_t> data;
            uint32_t dataLen = 0;
            if (ReadData(data, dataLen, TEST_READ_TIMEOUT_MS)) {
                PrintReceivedData(data, dataLen);
            }
        }
        std::cout << "Read thread exiting.\n";
    }

    void PrintReceivedData(const std::vector<int8_t>& data, uint32_t dataLen)
    {
        std::cout << "\n[Received " << dataLen << " bytes]: ";
        for (size_t i = 0; i < data.size() && i < TEST_PRINT_DATA_MAX_LEN; ++i) {
            char c = static_cast<char>(data[i]);
            std::cout << (c >= TEST_ASCII_MIN && c <= TEST_ASCII_MAX ? c : '.');
        }
        std::cout << "\n";
    }
private:
    sptr<ISerials> serials_ = nullptr;
    sptr<ISerialDevice> device_ = nullptr;
    sptr<SerialDeviceCallbackImpl> callback_ = nullptr;
    std::vector<SerialDeviceInfo> devices_;
    std::atomic<bool> running_;
    std::atomic<bool> readThreadRunning_;
    std::thread readThread_;
};

void DisplayMainMenu()
{
    std::cout << "\n========== Serial Sample Test ==========\n";
    std::cout << "1.Init 2.Query 3.Config 4.Open 5.Close\n";
    std::cout << "6.StartRead 7.StopRead 8.Write 9.Flush\n";
    std::cout << "10.Drain 11.Break 12.RTS 13.CTS 14.Echo 0.Exit\n";
    std::cout << "========================================\n";
}

void RunEchoTest(SerialSampleTest& test)
{
    std::cout << "\nEnter data to send: ";
    std::string input;
    std::cin.ignore();
    std::getline(std::cin, input);
    if (input == "quit") {
        return;
    }

    std::vector<uint8_t> data(input.begin(), input.end());
    if (test.WriteData(data)) {
        std::this_thread::sleep_for(std::chrono::milliseconds(TEST_SLEEP_TIME_MS));
        std::vector<int8_t> received;
        uint32_t dataLen = 0;
        if (test.ReadData(received, dataLen, TEST_ECHO_READ_TIMEOUT_MS)) {
            std::cout << "Echo: ";
            for (auto byte : received) {
                std::cout << static_cast<char>(byte);
            }
            std::cout << "\n";
        } else {
            std::cout << "No echo (timeout).\n";
        }
    }
}

void HandleConfigMenu(SerialConfigMenu& configMenu, SerialConfig& currentConfig, bool& configReady)
{
    int configChoice;
    do {
        configMenu.DisplayMenu();
        std::cout << "Choice: ";
        std::cin >> configChoice;
        if (configChoice == MENU_APPLY_CHOICE) {
            currentConfig = configMenu.GetConfig();
            configReady = true;
        } else if (configChoice == MENU_RESET_CHOICE) {
            configMenu.ResetToDefaults();
        }
        configMenu.HandleMenuChoice(configChoice);
    } while (configChoice != MENU_EXIT_CHOICE);
}

void HandleOpenDevice(SerialSampleTest& test, const SerialConfig& currentConfig,
    bool configReady, SerialConfigMenu& configMenu)
{
    int deviceIndex = 0;
    std::cout << "Device index (default 0): ";
    std::cin >> deviceIndex;

    SerialConfig config = configReady ? currentConfig : configMenu.GetConfig();
    test.OpenDevice(deviceIndex, config);
}

void HandleWriteData(SerialSampleTest& test)
{
    std::string data;
    std::cout << "Data to write: ";
    std::cin.ignore();
    std::getline(std::cin, data);
    test.WriteData(std::vector<uint8_t>(data.begin(), data.end()));
}

void HandleRtsSignal(SerialSampleTest& test)
{
    int rtsChoice;
    std::cout << "RTS (0=low, 1=high): ";
    std::cin >> rtsChoice;
    test.SetRts(rtsChoice == 1);
}

void HandleCtsSignal(SerialSampleTest& test)
{
    bool cts = false;
    if (test.GetCts(cts)) {
        std::cout << "CTS: " << (cts ? "high" : "low") << "\n";
    }
}

void HandleEchoTest(SerialSampleTest& test)
{
    if (!test.IsDeviceOpen()) {
        std::cout << "Please open device first.\n";
        return;
    }
    if (!test.IsReadThreadRunning()) {
        test.StartReadThread();
        std::this_thread::sleep_for(std::chrono::milliseconds(TEST_SLEEP_TIME_MS));
    }
    RunEchoTest(test);
}

void ProcessUserChoice(int choice, SerialSampleTest& test, SerialConfigMenu& configMenu,
    SerialConfig& currentConfig, bool& configReady)
{
    switch (choice) {
        case MENU_CHOICE_MIN: test.Init(); break;
        case 2: test.QueryDevices(); break;
        case 3: HandleConfigMenu(configMenu, currentConfig, configReady); break;
        case 4: HandleOpenDevice(test, currentConfig, configReady, configMenu); break;
        case 5: test.CloseDevice(); break;
        case 6: test.StartReadThread(); break;
        case 7: test.StopReadThread(); break;
        case 8: HandleWriteData(test); break;
        case 9: test.Flush(); break;
        case 10: test.Drain(); break;
        case 11: test.SendBreak(); break;
        case 12: HandleRtsSignal(test); break;
        case 13: HandleCtsSignal(test); break;
        case 14: HandleEchoTest(test); break;
        default: break;
    }
}

int main()
{
    SerialSampleTest test;
    SerialConfigMenu configMenu;
    SerialConfig currentConfig;
    bool configReady = false;


    while (true) {
        DisplayMainMenu();
        std::cout << "Choice: ";
        int choice;
        std::cin >> choice;

        if (choice == MENU_EXIT_CHOICE) {
            std::cout << "Exiting...\n";
            return MENU_EXIT_CHOICE;
        }

        ProcessUserChoice(choice, test, configMenu, currentConfig, configReady);

        if (test.IsDeviceOffline()) {
            std::cout << "\nDevice offline. Please reopen.\n";
        }
    }

    return MENU_EXIT_CHOICE;
}