当前位置:首页 >知识 >Openharmony南向研究(7) 比普通liunx响应速度要快

Openharmony南向研究(7) 比普通liunx响应速度要快

2024-07-01 10:29:34 [百科] 来源:避面尹邢网

Openharmony南向研究(7)-TCP(WPA)

作者:会飞的向研晨风君 系统 OpenHarmony 使用鸿蒙进行车机控制可以获得比较好的实时性,比普通liunx响应速度要快,向研代码体量子系统复杂度小,向研但是向研编译和代码测试时间久,各有优劣,向研目前完成的向研鸿蒙版本全向运动小车底盘还将加入更多功能,包括,向研视觉识别,向研人体跟踪等。向研

​​想了解更多关于开源的向研内容,请访问:​​

Openharmony南向研究(7) 比普通liunx响应速度要快

​​51CTO 开源基础软件社区​​

Openharmony南向研究(7) 比普通liunx响应速度要快

​​https://ost.51cto.com​​

Openharmony南向研究(7) 比普通liunx响应速度要快

基于标准系统的向研TCP和WAP使用-实现一个远程示波器

1、综述

近期在学习标准系统wifi能力子系统过程中同时也在调试一台鸿蒙小车底盘机,向研因为不能像以前调试STM32使用 Cube monitor进行方便的向研数据观测,进行PID和其他控制算法调参,向研完成小车的向研闭环自动控制,所以自己使用wpa子系统构建wifi访问,在本地widows主机上基于easyX构建了一套示波器用于PID参数调测,链路传输使用TCP进行数据传输。

2、关于标准系统南向设备应用程序开发的建议

一般对于新操作系统,尤其是类似OHOS这类不能够支持本地化编程,目前还没能移植VIM,GCC等工具进行非侵入式编程的操作系统,一般会使用linux本地机器进行程序验证,再加载到新操作系统的环境当中进行适配编译,这样可以有效减少编译浪费的时间。

3、WPA子系统配置自启动开发

(1)WPA子系统简介

OpenHarmony的wifi能力使用基于第三方开源wifi软件WPA-supplicant子系统完成,这部分在开源鸿蒙系统中进行了深度移植和融合

wpa_supplicant是跨平台的开源软件,支持WPA、WPA2、WPA3(IEEE 802.11i)等Wlan接入技术,可用于台式机、笔记本电脑、手机和其他嵌入式系统。

wpa_supplicant 支持多种网络管理器,如 NetworkManager 和 wicd,也可以通过命令行界面直接使用。用户可以通过编辑 wpa_supplicant 配置文件来配置连接的 Wi-Fi 网络的参数,例如 SSID、密码、加密类型等。

除了作为 Wi-Fi 客户端,wpa_supplicant 还支持将 Linux 计算机配置为 Wi-Fi 热点,使其他设备能够通过该计算机连接到 Wi-Fi 网络。

在OpenHarmony中,wpa_supplicant用来提供Wi-Fi接入和Wi-Fi热点开启的协议栈。

#创作者激励#【FFH】openharmony南向研究(7)-TCP(WPA)-开源基础软件社区

我们使用WPA相关的配置和设置来进行wifi配置.

接口层 :提供wpa_supplicant与Wi-Fi服务通信的接口。
wpa协议栈 :提供Wi-Fi接入与热点功能,其中wpa_supplicant负责Wi-Fi接入,hostapd负责热点功能。

我们主要使用接口层的能力,进行两种类型的配置:

  1. wifi连接 即本机设置为STA模式,连接到其他热点。
  2. 热点模式 即本机设置为AP模式,其他设备可以链接到本机。

配置为任意一种形式取决于当前场景的需要接下来对这两种配置模式进行分析和实操。

(2)2STA模式

此处需要wpa配置文件从而使能联网配置,如果使用STA模式需要创建一个 wpa_supplicant.conf 配置文件其中包含要连接的 Wi-Fi 网络的 SSID 和密码等信息

network={ 
ssid="your_SSID"
psk="your_password"
}

注意替换 your_SSID 和 your_password 为你要连接的 Wi-Fi 网络的 SSID 和密码。

在文件处理阶段有个核心问题,如果我们想把WIFI配置作为初始文件写入系统,无法在编码阶段创造好该文件,在打包结束后我们自己写的定义设置文件会丢失或者无法找到相对路径,这时候可以通过重新添加文件分区的方式进行添加,需要用到如下工具,当然如果非必须要开机初始化,可以使用hdcstd工具send功能将文件传输到系统当中,并作为参数文件调用。

natinusala/linux-amlogic-toolkit: Allows to unpack and repack AMLogic images for Android 7 without the Customization Tool (github.com)。

如果重新创建分区要使用到芯片厂商的打包工具以及开源打包工具mtd 通过以下指令安装和打包,打包后可以将原本的Openharmony.img通过上述工具amologic-toolkits进行解包,再重写进行打包,完成后可以重新烧录。

sudo apt-get install mtd-utils

sudo mkfs.jffs2 -o userfs.img -d userfs

(第二段指令将userfs文件打包为userfs.img镜像文件)。

在开发板上电后连接到终端控制台。

输入指令:

./bin/wpa_supplicant -i wlan0 -c /userfs/wpa_supplicant.conf

执行wpa_supplicant 脚本,配置conf文件到系统中,连接到指定网络。

(3)AP模式(未测试)

将conf文件编辑为

ctrl_interface=/var/run/wpa_supplicant
ctrl_interface_group=0
update_config=1

network={
ssid="my_hotspot"
mode=2
frequency=2412
proto=RSN
key_mgmt=WPA-PSK
pairwise=CCMP TKIP
group=CCMP TKIP
psk="my_password"
}

重复在STA模式中配置的方式,用以下命令启动热点。

wpa_supplicant -B -i wlan0 -c userfs/wpa_supplicant.conf

-B 表示在后台执行 ,-i表示参数后跟无线接口名称, -c 是conf文件的路径。

(4)WPA命令行常用指令

wpa_supplicant -iwlan0 -Dnl80211 -c/etc/ambaipcam/IPC_Q313/config/wlan/wpa_supplicant.conf -B
#查询网卡状态
wpa_cli -p/var/run/wpa_supplicant -iwlan0 status
#搜索附近网络功能 no/ok
wpa_cli -i wlan0 scan
#搜索附近网络,并列出结果
wpa_cli -i wlan0 scan_result
#查看当前连接的是哪个网络
wpa_cli -i wlan0 list_network
#获取一个存储wifi结构的id,假设为1
wpa_cli -i wlan0 add_network
#设置ID为1的热点SSID
wpa_cli -i wlan0 set_network 1 ssid '"HO4428"'
#设置ID为1的热点的密码
wpa_cli -i wlan0 set_network 1 psk '"442701102"'
#设置加密方式,可以不加
wpa_cli -i wlan0 set_network 1 key_mgmt WPA2-PSK-CCMP
#启动连接ID为1的网络
wpa_cli -i wlan0 enable_network 1
#选择网络0(这样做的好处在于,可以取消先前的其它网络连接)
wpa_cli -i wlan0 select_network 1
#保存刚刚填写的wifi帐号,写入配置文件
wpa_cli -i wlan0 save_config
#请求自动分配IP地址,-b:后台(back) –i:指定接口 –q:获得续约后退出,如果想尽在前台运行,则去掉-b,加上-f
#不用不加,会自动分配
udhcpc -b -i wlan0 -q

(5)查询IP地址

使用ipconfig指令查询当前网络地址。

ipconfig

4、TCP 实现

(1)TCP基本概念和主要特点

TCP(Transmission Control Protocol)是一种传输层协议,用于在网络上可靠地传输数据。TCP 是一种面向连接的协议,使用三次握手建立连接,并使用四次挥手终止连接。

TCP 的主要特点包括:

  • 可靠性:TCP 通过序列号和确认应答机制,确保数据传输的可靠性和完整性。如果数据包在传输过程中丢失或损坏,TCP 会自动重传该数据包,直到接收方收到正确的数据为止。
  • 流量控制:TCP 使用滑动窗口机制来控制数据的流量,确保发送方和接收方之间的数据传输速度相匹配,防止数据包拥塞和丢失。
  • 拥塞控制:TCP 可以检测网络拥塞情况,并采取相应的措施,例如降低数据传输速度,以避免拥塞的发生。
  • 面向连接:TCP 使用三次握手建立连接,并使用四次挥手终止连接,确保数据传输的可靠性和完整性。

(2)使用C实现TCP的基本操作

在LINUX中使用socket通信方式的TCP协议来实现数据传输,需要分别实现服务器端,和客户端,在这次的样例实现当中,九联UnionPi开发板作为服务端,因为有多个设备需要接入。

TCP服务端样例代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 8080 // 监听的端口号
#define BUFFER_SIZE 1024 // 缓冲区大小

int main() {
int server_fd, new_socket, valread;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = { 0};
char *hello = "Hello from server";

// 创建 socket 文件描述符
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}

// 设置 socket 选项,允许地址重用
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
&opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}

// 设置地址族、端口号和 IP 地址
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);

// 绑定 socket 和地址
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}

// 监听连接
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}

// 等待新连接
if ((new_socket = accept(server_fd, (struct sockaddr *)&address,
(socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}

// 读取客户端发送的数据
valread = read(new_socket, buffer, BUFFER_SIZE);
printf("%s\n", buffer);

// 向客户端发送数据
send(new_socket, hello, strlen(hello), 0);
printf("Hello message sent\n");

return 0;
}

TCP客户端样例代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 8080 // 目标端口号
#define BUFFER_SIZE 1024 // 缓冲区大小

int main(int argc, char const *argv[]) {
int sock = 0, valread;
struct sockaddr_in serv_addr;
char *hello = "Hello from client";
char buffer[BUFFER_SIZE] = { 0};

// 创建 socket 文件描述符
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Socket creation error");
exit(EXIT_FAILURE);
}

// 设置目标地址族、端口号和 IP 地址
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);

// 将点分十进制 IP 地址转换为网络字节序
if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) {
perror("Invalid address/ Address not supported");
exit(EXIT_FAILURE);
}

// 连接到目标地址
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("Connection Failed");
exit(EXIT_FAILURE);
}

// 向服务端发送数据
send(sock, hello, strlen(hello), 0);
printf("Hello message sent\n");

// 读取服务端发送的数据
valread = read(sock, buffer, BUFFER_SIZE);
printf("%s\n", buffer);

return 0;
}

在案例中实现了简单的TCP客户端和服务端,客户端向服务端发送HELLO。

5、OHOS端实现线程通信部署和数据发送

(1)目录结构

在之前的开发中对于小车的IMU数据进行了收取,以及小车底盘,蓝牙模块等进行了控制,通过工程化设计,分别划分在不同的线程当中,并进行了低耦合高内聚的函数化设计。

├── CMakeLists.txt
├── main.c
├── main.h
├── pthread_usr.c
├── pthread_usr.h
├── serial_base.c
├── serial_base.h
├── serial_protocal.c
└── serial_protocal.h

usr_pthread 负责进行所有线程的实现,main负责线程入口控制和初始化设置。线程间使用消息队列进行通信,因为存在IMU加速度计等信息高频刷新需要进行数据低延迟发送。serial_base和serial_protocal分别实现了串口的底层驱动和串口基础协议。

(2)消息队列

消息队列样例演示线程1 向线程2发送数据:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <mqueue.h>

#define QUEUE_NAME "/my_queue" // 消息队列名称
#define MESSAGE_SIZE 256 // 消息大小
#define MAX_MESSAGES 10 // 消息队列中最大消息数
#define QUEUE_PERMISSIONS 0660 // 消息队列权限

void *sender_thread(void *arg);
void *receiver_thread(void *arg);

int main(int argc, char const *argv[]) {
mqd_t mq;
pthread_t sender, receiver;
int ret;

// 创建消息队列
struct mq_attr attr = {
.mq_flags = 0,
.mq_maxmsg = MAX_MESSAGES,
.mq_msgsize = MESSAGE_SIZE,
.mq_curmsgs = 0
};
mq_unlink(QUEUE_NAME); // 删除已存在的消息队列
mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, QUEUE_PERMISSIONS, &attr);
if (mq == -1) {
perror("mq_open");
exit(EXIT_FAILURE);
}

// 创建发送线程和接收线程
ret = pthread_create(&sender, NULL, sender_thread, &mq);
if (ret != 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
ret = pthread_create(&receiver, NULL, receiver_thread, &mq);
if (ret != 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}

// 等待线程退出
pthread_join(sender, NULL);
pthread_join(receiver, NULL);

// 关闭消息队列
mq_close(mq);
mq_unlink(QUEUE_NAME);

return 0;
}

void *sender_thread(void *arg) {
mqd_t mq = *(mqd_t *)arg;
char message[MESSAGE_SIZE];

for (int i = 0; i < MAX_MESSAGES; i++) {
sprintf(message, "Message %d", i);
if (mq_send(mq, message, strlen(message) + 1, 0) == -1) {
perror("mq_send");
exit(EXIT_FAILURE);
}
printf("Sent message: %s\n", message);
sleep(1);
}

pthread_exit(NULL);
}

void *receiver_thread(void *arg) {
mqd_t mq = *(mqd_t *)arg;
char message[MESSAGE_SIZE];
unsigned int priority;

for (int i = 0; i < MAX_MESSAGES; i++) {
if (mq_receive(mq, message, MESSAGE_SIZE, &priority) == -1) {
perror("mq_receive");
exit(EXIT_FAILURE);
}
printf("Received message: %s\n", message);
}

pthread_exit(NULL);
}

在所有的线程当中都实现了类似的消息队列结构来保证数据的完整性和实效性。

绑定了socket通信格式和维特智能提供的IMU SDK后架构如下,不详细展开描述,只描述与示波器相关部分。

#创作者激励#【FFH】openharmony南向研究(7)-TCP(WPA)-开源基础软件社区

#创作者激励#【FFH】openharmony南向研究(7)-TCP(WPA)-开源基础软件社区

(3)TCP线程

(暂时这部分还有一些BUG,以及没有完整封装,在构建完成后会重新更新代码仓库)。

通过一个线程使用socket通信获取PID控制器返回的数据存入发送缓冲区,再将其发送出去。

void *_cpp_tcp_client(void) { 

// 设置 socket 选项,允许地址重用
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
&opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}

// 设置地址族、端口号和 IP 地址
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);

// 绑定 socket 和地址
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}

// 监听连接
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
// 2. 发送数据,并接受客户端数据
char send_info[256] = { 0};

//int n = -129;
//char nums[10];
//itostr(nums, n);
// printf("%s\n", nums);
int out_tcp_client;
while (1) {
if (IMU_INIT_FLAG == 0) {
sleep(10);
continue;
}
// if(pid.out>0)out_tcp_client=pid.out-60;
// else out_tcp_client=pid.out+60;
itostr(send_info, (int) pid.ActualAngle);

send(sock_client, send_info, strlen(send_info) + 1, 0);
printf("pid_out: %f,send: %s\n", pid.out, send_info);
usleep(3000);
//char recv_info[50];
//recv(sock_client, recv_info, sizeof(recv_info), 0);
//printf("receive: %s\n", recv_info);
memset(send_info, '\0', 256);
}

// 3. 关闭客户端
close(sock_client);
return 0;

}

整套小车控制程序在实现小车的偏航角闭环,此时要发送到客户端示波器的数据是当前角度。

我们现在将pid.actangle发送到17.20.10.1。

server_addr.sin_addr.s_addr = inet_addr("172.20.10.1");

6、Windows端实现示波器

(1)使用easyX图形库绘制波形图

由于熟悉的绘制波形图的图形库easyX在windows VS才能提供有效支持,所以暂时使用Windows的easyX图形库进行绘制。

easyX图形库使用链接EasyX Graphics Library for C++。

#创作者激励#【FFH】openharmony南向研究(7)-TCP(WPA)-开源基础软件社区


#创作者激励#【FFH】openharmony南向研究(7)-TCP(WPA)-开源基础软件社区

![image-20230327203843023]

(2)使用Windows socket完成网络通信

#include <iostream>
#include <WinSock2.h>

#pragma comment(lib,"ws2_32.lib")

using namespace std;

int main(int argv, char* argc[])
{
//初始化
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);

//创建套接字
SOCKET clntSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

//向服务器发送消息
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充
sockAddr.sin_family = PF_INET;
sockAddr.sin_addr.S_un.S_addr = inet_addr("192.168.43.235");
sockAddr.sin_port = htons(8888);
connect(clntSock, (SOCKADDR*)& sockAddr, sizeof(SOCKADDR));
char szBuffer[MAXBYTE] = { 0 };
while (1) {
//接收服务器消息

recv(clntSock, szBuffer, MAXBYTE, NULL);

//输出接收到的数据
cout << "服务端:" << szBuffer << endl;

//向服务端发送消息
const char* str = "100";
send(clntSock, str, strlen(str) + sizeof(char), NULL);
Sleep(10);
}
//关闭套接字
closesocket(clntSock);

//终止dll
WSACleanup();

system("pause");

return 0;
}

(3)结合两者完成示波器

#创作者激励#【FFH】openharmony南向研究(7)-TCP(WPA)-开源基础软件社区

切分为两个文件并把从socket读取到的数据放入示波器中进行展示。

#创作者激励#【FFH】openharmony南向研究(7)-TCP(WPA)-开源基础软件社区

grsp.cpp:

#include"grsp.h"

void num2WSTR(double x, wchar_t*& buffer)
{
// NUM TO WSS TO WSTR
std::wstringstream ss;
ss << x;
size_t size = ss.str().length();
buffer = new wchar_t[size + 1];
ss >> buffer;
buffer[size] = 0;
}

// DynaWin 构造函数
DynaWin::DynaWin(double XLIM, double YLIM, unsigned short xSegment, unsigned short ySegment, int xResolution)
{
this->XLIM = XLIM;
this->YLIM = YLIM;
this->xSegment = xSegment;
this->ySegment = ySegment;
this->xResolution = xResolution;
this->DynaQue = new double[xResolution + 1];
this->xAxis = new AxisX[xSegment + 1];
}

//背景初始化
void DynaWin::InitBackGround()
{
// 定义画布
initgraph(1100, 400, EX_SHOWCONSOLE);
// 初始化画笔
setlinecolor(RED);
setlinestyle(PS_SOLID | PS_ENDCAP_ROUND | PS_JOIN_ROUND, 3);
// 定义坐标原点以及方向
setorigin(CENTERX, CENTERY);
setaspectratio(1, -1);
// 绘制静态窗口
rectangle(0, 150, 1000, -150);
line(0, 0, 1000, 0);
// 标注说明
setbkmode(TRANSPARENT);
settextcolor(YELLOW);
setaspectratio(1, 1);// 纠正字体倒置
settextstyle(30, 0, L"宋体");
outtextxy(450, -185, L"EasyPlot");
settextstyle(10, 0, L"宋体");
outtextxy(850, 160, L"Author:Dylan");
outtextxy(870, 180, L"Version:1.0");
// 绘制刻度
unsigned short yVertex = 2 * ySegment + 1;
double yUnit = YLIM / ySegment;
setbkmode(TRANSPARENT);
settextcolor(YELLOW);
settextstyle(15, 0, L"10");
setlinestyle(PS_DASH, 1); // 虚线
// 水平刻度线
for (int i = 1; i <= yVertex; ++i)
{
// 表明 y 刻度值
double label = YLIM - (i - 1) * yUnit;
wchar_t* buffer;
num2WSTR(label, buffer);
outtextxy(-30, (int)((i - ySegment - 1) * (ROWS / ySegment)) - 10, buffer);
delete[] buffer;
buffer = (wchar_t*)NULL;

// 画出虚线
line(0, (int)((i - ySegment - 1) * (ROWS / ySegment)), 1000, (int)((i - ySegment - 1) * (ROWS / ySegment)));
}
setaspectratio(1, -1); // 恢复坐标方向
canvas = GetWorkingImage(); // 画板地址
getimage(&local, -50, -160, 1100, 320); // 保存局部背景
}


// 更新窗
void DynaWin::UpateWin(double y) // 更新 win
{
++DynaNum; // 信息个数
UpdateAxisX(); // 更新AxisX

if (DynaNum <= xResolution + 1) // 队列未满
DynaQue[DynaNum - 1] = y;

else // 队列已满
{
for (int i = 0; i <= xResolution - 1; ++i) // 出队列
{
DynaQue[i] = DynaQue[i + 1]; // 向左移位
}
DynaQue[xResolution] = y; // 尾部添加新元素
}
}

// 绘制窗
void DynaWin::ShowWin()
{
// 选定画板
SetWorkingImage(canvas);

// 背景覆盖
putimage(-50, -160, &local);

// (1) 显示 AxisX
setlinecolor(RED);
setlinestyle(PS_DASH, 2);
for (int j = 0; j <= xSegment - 1; ++j)
{
// 绘制虚线
line((int)xAxis[j].index, -150, (int)xAxis[j].index, 150);
// 绘制标注
setaspectratio(1, 1);
setbkmode(TRANSPARENT);
settextcolor(YELLOW);
settextstyle(15, 0, L"10");
double label = xAxis[j].label;
wchar_t* wstr;
num2WSTR(label, wstr);
outtextxy((int)xAxis[j].index - 10, 0, wstr);
delete[] wstr;
wstr = NULL;
setaspectratio(1, -1); // 回复坐标系
}

// (2) 显示 DynaQue
setlinecolor(WHITE);
setlinestyle(PS_SOLID | PS_ENDCAP_ROUND | PS_JOIN_ROUND, 2);
if (DynaNum <= xResolution) // 队列未满
{
for (int i = 0; i <= DynaNum - 2; ++i) // 0 to DynaNum-1 (DynaNum 个)
{
line((int)(i * COLS / xResolution), (int)(DynaQue[i] / YLIM * ROWS), (int)((i + 1) * COLS / xResolution), (int)(DynaQue[i + 1] / YLIM * ROWS));
}
}
else // 队列已满
{
for (int i = 0; i <= xResolution - 1; ++i) // 0 to xResolution (xResolution + 1 个) 即显示全部 DynaQue
{
line((int)(i * COLS / xResolution), (int)(DynaQue[i] / YLIM * ROWS), (int)((i + 1) * COLS / xResolution), (int)(DynaQue[i + 1] / YLIM * ROWS));
}
}
}

// 更新坐标
void DynaWin::UpdateAxisX()
{
if (DynaNum <= xResolution) // 队列未满
for (int i = 0; i <= xSegment - 1; ++i)
{
xAxis[i].index = (i + 1) * COLS / xSegment;
xAxis[i].label = (i + 1) * XLIM / xSegment;
}
else // 队列已满
{

for (int i = 0; i <= xSegment - 1; ++i) // 向左移动
{
xAxis[i].index -= COLS / xResolution;
}
if (xAxis[0].index <= 0) // AxisX 动态更新
{
int j;
for (j = 0; j <= xSegment - 2; ++j) // 队列更替
{
xAxis[j].index = xAxis[j + 1].index;
xAxis[j].label = xAxis[j + 1].label;
}
xAxis[j].index = xAxis[j - 1].index + COLS / xSegment; // 尾部添加新元素
xAxis[j].label = xAxis[j - 1].label + XLIM / xSegment;
}
}
}

grsp.h:

#pragma once
#include <iostream>
#include <easyx.h>
#include <conio.h>
#include <sstream>
#include <string>
#define PI 3.141592

/

(责任编辑:时尚)

    推荐文章
    热点阅读