LCOV - code coverage report
Current view: top level - src/client - client_state.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 32.1 % 252 81
Test Date: 2025-12-19 03:13:09 Functions: 34.5 % 29 10

            Line data    Source code
       1              : /**
       2              :  * @file client_state.cpp
       3              :  * @brief 客户端状态管理实现
       4              :  */
       5              : 
       6              : #include "client_state.hpp"
       7              : #include <algorithm>
       8              : #include <chrono>
       9              : #include <iomanip>
      10              : #include <sstream>
      11              : #include <fstream>
      12              : #include <cstdlib>
      13              : 
      14              : namespace fix40::client {
      15              : 
      16              : // ============================================================================
      17              : // 连接状态
      18              : // ============================================================================
      19              : 
      20            0 : void ClientState::setConnectionState(ConnectionState state) {
      21            0 :     connectionState_.store(state);
      22            0 :     notifyStateChange();
      23            0 : }
      24              : 
      25            0 : ConnectionState ClientState::getConnectionState() const {
      26            0 :     return connectionState_.load();
      27              : }
      28              : 
      29            0 : std::string ClientState::getConnectionStateString() const {
      30            0 :     switch (connectionState_.load()) {
      31            0 :         case ConnectionState::DISCONNECTED: return "断开连接";
      32            0 :         case ConnectionState::CONNECTING:   return "连接中...";
      33            0 :         case ConnectionState::CONNECTED:    return "已连接";
      34            0 :         case ConnectionState::LOGGING_IN:   return "登录中...";
      35            0 :         case ConnectionState::LOGGED_IN:    return "已登录";
      36            0 :         case ConnectionState::ERROR:        return "错误";
      37            0 :         default: return "未知";
      38              :     }
      39              : }
      40              : 
      41            0 : void ClientState::setUserId(const std::string& userId) {
      42            0 :     std::lock_guard<std::mutex> lock(mutex_);
      43            0 :     userId_ = userId;
      44            0 : }
      45              : 
      46            0 : std::string ClientState::getUserId() const {
      47            0 :     std::lock_guard<std::mutex> lock(mutex_);
      48            0 :     return userId_;
      49            0 : }
      50              : 
      51              : // ============================================================================
      52              : // 账户信息
      53              : // ============================================================================
      54              : 
      55            2 : void ClientState::updateAccount(const AccountInfo& info) {
      56              :     {
      57            2 :         std::lock_guard<std::mutex> lock(mutex_);
      58            2 :         account_ = info;
      59            2 :     }
      60            2 :     notifyStateChange();
      61            2 : }
      62              : 
      63            2 : AccountInfo ClientState::getAccount() const {
      64            2 :     std::lock_guard<std::mutex> lock(mutex_);
      65            2 :     return account_;
      66            2 : }
      67              : 
      68              : // ============================================================================
      69              : // 持仓信息
      70              : // ============================================================================
      71              : 
      72            2 : void ClientState::updatePosition(const PositionInfo& pos) {
      73            2 :     bool updated = false;
      74              :     {
      75            2 :         std::lock_guard<std::mutex> lock(mutex_);
      76            2 :         auto it = std::find_if(positions_.begin(), positions_.end(),
      77            2 :             [&](const PositionInfo& p) { return p.instrumentId == pos.instrumentId; });
      78            2 :         if (it != positions_.end()) {
      79              :             // 合并更新:推送消息可能包含0数量(用于“平到0后清空持仓”)。
      80              :             // 仅当 quantitiesValid=true 时才认为数量字段可用(即使为0也要应用/删除)。
      81            2 :             if (pos.quantitiesValid && pos.longPosition == 0 && pos.shortPosition == 0) {
      82              :                 // 持仓已清空:移除该合约持仓,避免 UI 停留在平仓前的最后一刻。
      83            1 :                 positions_.erase(it);
      84            1 :                 updated = true;
      85              :             } else {
      86            1 :                 if (pos.quantitiesValid) {
      87            0 :                     it->longPosition = pos.longPosition;
      88            0 :                     it->shortPosition = pos.shortPosition;
      89              :                 }
      90            1 :                 if (pos.longAvgPrice > 0) it->longAvgPrice = pos.longAvgPrice;
      91            1 :                 if (pos.shortAvgPrice > 0) it->shortAvgPrice = pos.shortAvgPrice;
      92              :                 // 盈亏始终更新(可能为负)
      93            1 :                 it->profit = pos.profit;
      94            1 :                 updated = true;
      95              :             }
      96              :         } else {
      97              :             // 新增持仓:若推送的是“0数量清空”则忽略即可。
      98            0 :             if (!(pos.quantitiesValid && pos.longPosition == 0 && pos.shortPosition == 0)) {
      99            0 :                 positions_.push_back(pos);
     100            0 :                 updated = true;
     101              :             }
     102              :         }
     103            2 :     }
     104            2 :     if (updated) {
     105            2 :         notifyStateChange();
     106              :     }
     107            2 : }
     108              : 
     109            2 : void ClientState::setPositions(const std::vector<PositionInfo>& positions) {
     110              :     {
     111            2 :         std::lock_guard<std::mutex> lock(mutex_);
     112            2 :         positions_ = positions;
     113            2 :     }
     114            2 :     notifyStateChange();
     115            2 : }
     116              : 
     117            2 : std::vector<PositionInfo> ClientState::getPositions() const {
     118            2 :     std::lock_guard<std::mutex> lock(mutex_);
     119            4 :     return positions_;
     120            2 : }
     121              : 
     122            0 : void ClientState::clearPositions() {
     123              :     {
     124            0 :         std::lock_guard<std::mutex> lock(mutex_);
     125            0 :         positions_.clear();
     126            0 :     }
     127            0 :     notifyStateChange();
     128            0 : }
     129              : 
     130              : // ============================================================================
     131              : // 订单信息
     132              : // ============================================================================
     133              : 
     134            2 : void ClientState::addOrder(const OrderInfo& order) {
     135              :     {
     136            2 :         std::lock_guard<std::mutex> lock(mutex_);
     137            2 :         if (orders_.find(order.clOrdID) == orders_.end()) {
     138            2 :             orderSequence_.push_back(order.clOrdID);
     139              :         }
     140            2 :         orders_[order.clOrdID] = order;
     141            2 :     }
     142            2 :     notifyStateChange();
     143            2 : }
     144              : 
     145            0 : void ClientState::updateOrder(const std::string& clOrdID, const OrderInfo& order) {
     146              :     {
     147            0 :         std::lock_guard<std::mutex> lock(mutex_);
     148            0 :         if (orders_.find(clOrdID) == orders_.end()) {
     149            0 :             orderSequence_.push_back(clOrdID);
     150              :         }
     151            0 :         orders_[clOrdID] = order;
     152            0 :     }
     153            0 :     notifyStateChange();
     154            0 : }
     155              : 
     156            1 : std::vector<OrderInfo> ClientState::getOrders() const {
     157            1 :     std::lock_guard<std::mutex> lock(mutex_);
     158            1 :     std::vector<OrderInfo> result;
     159            1 :     result.reserve(orderSequence_.size());
     160            3 :     for (const auto& clOrdID : orderSequence_) {
     161            2 :         auto it = orders_.find(clOrdID);
     162            2 :         if (it != orders_.end()) {
     163            2 :             result.push_back(it->second);
     164              :         }
     165              :     }
     166            2 :     return result;
     167            1 : }
     168              : 
     169            0 : std::vector<OrderInfo> ClientState::getActiveOrders() const {
     170            0 :     std::lock_guard<std::mutex> lock(mutex_);
     171            0 :     std::vector<OrderInfo> result;
     172            0 :     result.reserve(orderSequence_.size());
     173            0 :     for (const auto& clOrdID : orderSequence_) {
     174            0 :         auto it = orders_.find(clOrdID);
     175            0 :         if (it == orders_.end()) {
     176            0 :             continue;
     177              :         }
     178            0 :         const auto& order = it->second;
     179            0 :         if (order.state == OrderState::PENDING_NEW ||
     180            0 :             order.state == OrderState::NEW ||
     181            0 :             order.state == OrderState::PARTIALLY_FILLED) {
     182            0 :             result.push_back(order);
     183              :         }
     184              :     }
     185            0 :     return result;
     186            0 : }
     187              : 
     188            0 : void ClientState::clearOrders() {
     189              :     {
     190            0 :         std::lock_guard<std::mutex> lock(mutex_);
     191            0 :         orders_.clear();
     192            0 :         orderSequence_.clear();
     193            0 :     }
     194            0 :     notifyStateChange();
     195            0 : }
     196              : 
     197            0 : void ClientState::setOrders(const std::vector<OrderInfo>& orders) {
     198              :     {
     199            0 :         std::lock_guard<std::mutex> lock(mutex_);
     200            0 :         orders_.clear();
     201            0 :         orderSequence_.clear();
     202            0 :         orderSequence_.reserve(orders.size());
     203            0 :         for (const auto& order : orders) {
     204            0 :             if (order.clOrdID.empty()) {
     205            0 :                 continue;
     206              :             }
     207            0 :             orderSequence_.push_back(order.clOrdID);
     208            0 :             orders_[order.clOrdID] = order;
     209              :         }
     210            0 :     }
     211            0 :     notifyStateChange();
     212            0 : }
     213              : 
     214            0 : static std::string getDefaultOrdersPath() {
     215            0 :     const char* home = std::getenv("HOME");
     216            0 :     if (home) {
     217            0 :         return std::string(home) + "/.fix_client_orders.dat";
     218              :     }
     219            0 :     return ".fix_client_orders.dat";
     220              : }
     221              : 
     222            0 : void ClientState::saveOrders(const std::string& filepath) {
     223            0 :     std::string path = filepath.empty() ? getDefaultOrdersPath() : filepath;
     224            0 :     std::lock_guard<std::mutex> lock(mutex_);
     225              :     
     226            0 :     std::ofstream ofs(path);
     227            0 :     if (!ofs) return;
     228              :     
     229              :     // 简单的文本格式:每行一个订单,字段用 | 分隔
     230            0 :     for (const auto& clOrdID : orderSequence_) {
     231            0 :         auto it = orders_.find(clOrdID);
     232            0 :         if (it == orders_.end()) {
     233            0 :             continue;
     234              :         }
     235            0 :         const auto& order = it->second;
     236            0 :         ofs << order.clOrdID << "|"
     237            0 :             << order.orderId << "|"
     238            0 :             << order.symbol << "|"
     239            0 :             << order.side << "|"
     240            0 :             << order.price << "|"
     241            0 :             << order.orderQty << "|"
     242            0 :             << order.filledQty << "|"
     243            0 :             << order.avgPx << "|"
     244            0 :             << static_cast<int>(order.state) << "|"
     245            0 :             << order.text << "|"
     246            0 :             << order.updateTime << "\n";
     247              :     }
     248            0 : }
     249              : 
     250            0 : void ClientState::loadOrders(const std::string& filepath) {
     251            0 :     std::string path = filepath.empty() ? getDefaultOrdersPath() : filepath;
     252              :     
     253            0 :     std::ifstream ifs(path);
     254            0 :     if (!ifs) return;
     255              :     
     256            0 :     std::lock_guard<std::mutex> lock(mutex_);
     257            0 :     orders_.clear();
     258            0 :     orderSequence_.clear();
     259              :     
     260            0 :     std::string line;
     261            0 :     while (std::getline(ifs, line)) {
     262            0 :         if (line.empty()) continue;
     263              :         
     264            0 :         std::vector<std::string> fields;
     265            0 :         std::istringstream iss(line);
     266            0 :         std::string field;
     267            0 :         while (std::getline(iss, field, '|')) {
     268            0 :             fields.push_back(field);
     269              :         }
     270              :         
     271              :         // 至少需要 9 个字段(到 state)
     272            0 :         if (fields.size() >= 9) {
     273              :             try {
     274            0 :                 OrderInfo order;
     275            0 :                 order.clOrdID = fields[0];
     276            0 :                 order.orderId = fields[1];
     277            0 :                 order.symbol = fields[2];
     278            0 :                 order.side = fields[3];
     279            0 :                 order.price = std::stod(fields[4]);
     280            0 :                 order.orderQty = std::stoll(fields[5]);
     281            0 :                 order.filledQty = std::stoll(fields[6]);
     282            0 :                 order.avgPx = std::stod(fields[7]);
     283            0 :                 order.state = static_cast<OrderState>(std::stoi(fields[8]));
     284            0 :                 if (fields.size() > 9) order.text = fields[9];
     285            0 :                 if (fields.size() > 10) order.updateTime = fields[10];
     286            0 :                 orders_[order.clOrdID] = order;
     287            0 :                 orderSequence_.push_back(order.clOrdID);
     288            0 :             } catch (...) {
     289              :                 // 解析失败,跳过该条目
     290            0 :             }
     291              :         }
     292            0 :     }
     293              : 
     294              :     // 旧版本保存顺序来自 unordered_map,可能是“随机”的。
     295              :     // 这里按 clOrdID 做一次稳定排序,至少能让展示顺序可预测(也更接近下单序号)。
     296            0 :     std::sort(orderSequence_.begin(), orderSequence_.end());
     297            0 :     orderSequence_.erase(std::unique(orderSequence_.begin(), orderSequence_.end()),
     298            0 :                          orderSequence_.end());
     299            0 : }
     300              : 
     301              : // ============================================================================
     302              : // 合约搜索结果
     303              : // ============================================================================
     304              : 
     305            0 : void ClientState::setSearchResults(const std::vector<std::string>& results) {
     306              :     {
     307            0 :         std::lock_guard<std::mutex> lock(mutex_);
     308            0 :         searchResults_ = results;
     309            0 :     }
     310            0 :     notifyStateChange();
     311            0 : }
     312              : 
     313            0 : std::vector<std::string> ClientState::getSearchResults() const {
     314            0 :     std::lock_guard<std::mutex> lock(mutex_);
     315            0 :     return searchResults_;
     316            0 : }
     317              : 
     318              : // ============================================================================
     319              : // 状态变更通知
     320              : // ============================================================================
     321              : 
     322            0 : void ClientState::setOnStateChange(StateChangeCallback callback) {
     323            0 :     std::lock_guard<std::mutex> lock(mutex_);
     324            0 :     onStateChange_ = std::move(callback);
     325            0 : }
     326              : 
     327           10 : void ClientState::notifyStateChange() {
     328           10 :     StateChangeCallback callback;
     329              :     {
     330           10 :         std::lock_guard<std::mutex> lock(mutex_);
     331              :         
     332              :         // 节流:最多每 50ms 通知一次,避免高频刷新导致崩溃
     333           10 :         auto now = std::chrono::steady_clock::now();
     334           10 :         auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - lastNotifyTime_);
     335           10 :         if (elapsed.count() < 50) {
     336            6 :             return;
     337              :         }
     338            4 :         lastNotifyTime_ = now;
     339              :         
     340            4 :         callback = onStateChange_;
     341           10 :     }
     342            4 :     if (callback) {
     343            0 :         callback();
     344              :     }
     345           10 : }
     346              : 
     347              : // ============================================================================
     348              : // 消息/错误
     349              : // ============================================================================
     350              : 
     351            0 : void ClientState::setLastError(const std::string& error) {
     352              :     {
     353            0 :         std::lock_guard<std::mutex> lock(mutex_);
     354            0 :         lastError_ = error;
     355            0 :     }
     356            0 :     notifyStateChange();
     357            0 : }
     358              : 
     359            0 : std::string ClientState::getLastError() const {
     360            0 :     std::lock_guard<std::mutex> lock(mutex_);
     361            0 :     return lastError_;
     362            0 : }
     363              : 
     364            2 : void ClientState::addMessage(const std::string& msg) {
     365              :     {
     366            2 :         std::lock_guard<std::mutex> lock(mutex_);
     367              :         // 获取当前时间
     368            2 :         auto now = std::chrono::system_clock::now();
     369            2 :         auto time = std::chrono::system_clock::to_time_t(now);
     370            2 :         std::ostringstream oss;
     371            2 :         std::tm tm_buf{};
     372              : #if defined(_WIN32)
     373              :         localtime_s(&tm_buf, &time);
     374              : #else
     375            2 :         localtime_r(&time, &tm_buf);
     376              : #endif
     377            2 :         oss << std::put_time(&tm_buf, "%H:%M:%S") << " " << msg;
     378            2 :         messages_.push_back(oss.str());
     379              :         // 保留最近 100 条消息
     380            2 :         if (messages_.size() > 100) {
     381            0 :             messages_.erase(messages_.begin());
     382              :         }
     383            2 :     }
     384            2 :     notifyStateChange();
     385            2 : }
     386              : 
     387            0 : std::vector<std::string> ClientState::getMessages() const {
     388            0 :     std::lock_guard<std::mutex> lock(mutex_);
     389            0 :     return messages_;
     390            0 : }
     391              : 
     392              : } // namespace fix40::client
        

Generated by: LCOV version 2.0-1