VREP Remote API工作模式详解(未写完,完成度90%)
参考官方文档链接(可能需要梯子):http://www.coppeliarobotics.com/helpFiles/index.html
文章目录
- 概述
- 阻塞式函数调用模式(Blocking function calls)
- 例1:读取UR5机械臂转轴句柄
- 非阻塞式函数调用模式(Non-blocking function calls)
- 例2:置位转轴角度
- 数据流模式(Data streaming)
- 例3:读取UR5机械臂转轴角度
- 同步模式(Synchronous operation)
- 附加内容
概述
remote API的使用方式与regularAPI类似,但是有2点不同:
- 大多remote API都会返回一个位编码值:
return code
。因为return code
是bit-coded的,所以需要测试每一个位来确定正确的含义。 - 大多remote API都需要两个额外的参数:
operation mode
和clientID
(simxStart
返回的标识符)。
需要operation mode
和return code
的原因是remote API函数需要通过socket通信机制从客户端到服务端(VREP),执行任务,返回客户端。一种简单的(或常规的)方法是让客户机发送请求,然后等待服务器处理请求并作出响应:在大多数情况下,这会花费太多的时间,而且延迟会损害客户机应用程序。实际上,remote API通过提供四种机制来执行函数或控制仿真过程,让用户选择operation mode和仿真进行的方式。
- 阻塞式函数调用模式(Blocking function calls)
- 非阻塞时函数调用模式(Blocking function calls)
- 数据流模式(Data streaming)
- 同步模式(Synchronous operation)
阻塞式函数调用模式(Blocking function calls)
阻塞式函数调用模式是一种简单常规的方式,适用于必须等待从服务端(VREP)返回信息的情形,比如如下情况:
// Following function (blocking mode) will retrieve an object handle:
if (simxGetObjectHandle(clientID,"myJoint",&jointHandle,simx_opmode_blocking)==simx_return_ok)
{// here we have the joint handle in variable jointHandle!
}
下图阐明了阻塞式函数调用模式:
例1:读取UR5机械臂转轴句柄
详情:在场景中有一个UR5机械臂,以下代码读取机械臂各个轴的句柄值。
配套scene文件:
链接:https://pan.baidu.com/s/1cAUe15T7FMDWrGgUjLm9aw
提取码:2w75
与下面例3场景一致。另外这个场景非常简单,就只是在空场景里拖拽一个UR5过去。自己操作就好,实在不放心可以再下载。
代码:
import vrepvrep.simxFinish(-1) # just in case, close all opened connections
clientID=vrep.simxStart( # clientID,经测试从0计数,若超时返回-1。若不返回-1,则应该在程序最后调用 simxFinish'127.0.0.1', # 服务端(server)的IP地址,本机为127.0.0.119997, # 端口号True, # True:程序等待服务端开启(或连接超时)True, # True:连接丢失时,通信线程不会尝试第二次连接2000, # 正:超时时间(ms)(此时阻塞函数时间为5s)负:阻塞函数时间(ms)(此时连接等待时间为5s)5) # 数据传输间隔,越小越快,默认5 # Connect to V-REPprint('Connected to remote API server')vrep.simxStartSimulation(clientID, vrep.simx_opmode_oneshot)RC1, UR5_joint1_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint1', vrep.simx_opmode_blocking)
RC2, UR5_joint2_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint2', vrep.simx_opmode_blocking)
RC3, UR5_joint3_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint3', vrep.simx_opmode_blocking)
RC4, UR5_joint4_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint4', vrep.simx_opmode_blocking)
RC5, UR5_joint5_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint5', vrep.simx_opmode_blocking)
RC6, UR5_joint6_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint6', vrep.simx_opmode_blocking)
print("RC1:",RC1)
print("UR5_joint1_Handle",UR5_joint1_Handle)
print("RC2:",RC1)
print("UR5_joint2_Handle",UR5_joint2_Handle)
print("RC3:",RC1)
print("UR5_joint3_Handle",UR5_joint3_Handle)
print("RC4:",RC1)
print("UR5_joint4_Handle",UR5_joint4_Handle)
print("RC5:",RC1)
print("UR5_joint5_Handle",UR5_joint5_Handle)
print("RC1:",RC1)
print("UR6_joint6_Handle",UR5_joint6_Handle)
非阻塞式函数调用模式(Non-blocking function calls)
非阻塞函数调用模式用在仅仅想给服务端(VREP)发送指令,而无需等待服务端返回信息的情况,例如如下情形:
// Following function (non-blocking mode) will set the position of a joint:
simxSetJointPosition(clientID,jointHandle,jointPosition,simx_opmode_oneshot);
下图阐明了非阻塞式函数调用模式:
例2:置位转轴角度
详情:在场景中有一个转轴(passive mode)A,连接着另一个转轴B。要把转轴A旋转1rad。
配套scene文件:
链接:https://pan.baidu.com/s/1S5JPmIl_fz5qiovHEncINw
提取码:4scy
代码:
import vrepvrep.simxFinish(-1) # just in case, close all opened connections
clientID=vrep.simxStart( # clientID,经测试从0计数,若超时返回-1。若不返回-1,则应该在程序最后调用 simxFinish'127.0.0.1', # 服务端(server)的IP地址,本机为127.0.0.119997, # 端口号True, # True:程序等待服务端开启(或连接超时)True, # True:连接丢失时,通信线程不会尝试第二次连接2000, # 正:超时时间(ms)(此时阻塞函数时间为5s)负:阻塞函数时间(ms)(此时连接等待时间为5s)5) # 数据传输间隔,越小越快,默认5 # Connect to V-REPprint('Connected to remote API server')vrep.simxStartSimulation(clientID, vrep.simx_opmode_oneshot)
RC0, h= vrep.simxGetObjectHandle(clientID, 'j', vrep.simx_opmode_blocking)vrep.simxSetJointPosition(clientID,h,1,vrep.simx_opmode_oneshot)vrep.simxGetPingTime(clientID) # 不可少,否则很可能不执行,后面会解释为什么
有些情形下,用一条指令传送多条信息是很重要的——这些信息会在服务端同时执行(例如让机器人的3个关节同时运动,即,在同一个仿真步中)。在这种情况下,用户可以暂停通信进程来实现,如下所示:
simxPauseCommunication(clientID,1);
simxSetJointPosition(clientID,joint1Handle,joint1Value,simx_opmode_oneshot);
simxSetJointPosition(clientID,joint2Handle,joint2Value,simx_opmode_oneshot);
simxSetJointPosition(clientID,joint3Handle,joint3Value,simx_opmode_oneshot);
simxPauseCommunication(clientID,0);// Above's 3 joints will be received and set on the V-REP side at the same time
下图阐明了暂停通信进程的效果:
数据流模式(Data streaming)
服务端可以预测客户端需求的数据类型。要实现这一点,客户端必须用流(streaming)
或者连续(continuous)
操作模式flag向服务端发出此请求(即:函数被存放在服务端,在不需要客户端发出请求的情况下,定期执行并发送数据)。这可以看做是从客户端到服务端的命令(command)/信息(message)
订阅,其中服务端像客户端提供数据流。在客户端这种数据流操作请求和读取流数据如下所示:
// Streaming operation request (subscription) (function returns immediately (non-blocking)):
simxGetJointPosition(clientID,jointHandle,&jointPosition,simx_opmode_streaming);// The control loop:
while (simxGetConnectionId(clientID)!=-1) // while we are connected to the server..
{ // Fetch the newest joint value from the inbox (func. returns immediately (non-blocking)):if (simxGetJointPosition(clientID,jointHandle,&jointPosition,simx_opmode_buffer)==simx_return_ok) { // here we have the newest joint position in variable jointPosition! }else{// once you have enabled data streaming, it will take a few ms until the first value has arrived. So if// we landed in this code section, this does not always mean we have an error!!!}
}// Streaming operation is enabled/disabled individually for each command and
// object(s) the command applies to. In above case, only the joint position of
// the joint with handle jointHandle will be streamed.
下图阐明了数据流操作模式:
数据流提取完后,要通知服务端停止数据流传输,否则服务端将一直传送无用数据,并导致速度下降。用simx_opmode_discontinue来实现停止传输。
例3:读取UR5机械臂转轴角度
详情:在场景中有一个UR5机械臂,以下代码读取机械臂各个轴的角度值。
配套scene文件:
链接:https://pan.baidu.com/s/1cAUe15T7FMDWrGgUjLm9aw
提取码:2w75
与上面例1场景一致。另外这个场景非常简单,就只是在空场景里拖拽一个UR5过去。自己操作就好,实在不放心可以再下载。
代码:
import vrep
import timevrep.simxFinish(-1) # just in case, close all opened connections
clientID=vrep.simxStart( # clientID,经测试从0计数,若超时返回-1。若不返回-1,则应该在程序最后调用 simxFinish'127.0.0.1', # 服务端(server)的IP地址,本机为127.0.0.119997, # 端口号True, # True:程序等待服务端开启(或连接超时)True, # True:连接丢失时,通信线程不会尝试第二次连接2000, # 正:超时时间(ms)(此时阻塞函数时间为5s)负:阻塞函数时间(ms)(此时连接等待时间为5s)!不太理解!5) # 数据传输间隔,越小越快,默认5 # Connect to V-REPprint('Connected to remote API server')vrep.simxStartSimulation(clientID, vrep.simx_opmode_oneshot)RC1, UR5_joint1_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint1', vrep.simx_opmode_blocking)
RC2, UR5_joint2_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint2', vrep.simx_opmode_blocking)
RC3, UR5_joint3_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint3', vrep.simx_opmode_blocking)
RC4, UR5_joint4_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint4', vrep.simx_opmode_blocking)
RC5, UR5_joint5_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint5', vrep.simx_opmode_blocking)
RC6, UR5_joint6_Handle = vrep.simxGetObjectHandle(clientID, 'UR5_joint6', vrep.simx_opmode_blocking)vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_streaming)
vrep.simxGetJointPosition(clientID, UR5_joint2_Handle, vrep.simx_opmode_streaming)
vrep.simxGetJointPosition(clientID, UR5_joint3_Handle, vrep.simx_opmode_streaming)
vrep.simxGetJointPosition(clientID, UR5_joint4_Handle, vrep.simx_opmode_streaming)
vrep.simxGetJointPosition(clientID, UR5_joint5_Handle, vrep.simx_opmode_streaming)
vrep.simxGetJointPosition(clientID, UR5_joint6_Handle, vrep.simx_opmode_streaming)while(True):if (vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_buffer)[0] # 判断vrep是否开始回传数据& vrep.simxGetJointPosition(clientID, UR5_joint2_Handle, vrep.simx_opmode_buffer)[0] # [0]是指return中的第0位,也即return code& vrep.simxGetJointPosition(clientID, UR5_joint2_Handle, vrep.simx_opmode_buffer)[0]& vrep.simxGetJointPosition(clientID, UR5_joint2_Handle, vrep.simx_opmode_buffer)[0]& vrep.simxGetJointPosition(clientID, UR5_joint2_Handle, vrep.simx_opmode_buffer)[0]& vrep.simxGetJointPosition(clientID, UR5_joint2_Handle, vrep.simx_opmode_buffer)[0])==vrep.simx_return_ok:for i in range(3): # 提取3次关节角度rc1, j1_pos=vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_buffer)rc2, j2_pos=vrep.simxGetJointPosition(clientID, UR5_joint2_Handle, vrep.simx_opmode_buffer)rc3, j3_pos=vrep.simxGetJointPosition(clientID, UR5_joint3_Handle, vrep.simx_opmode_buffer)rc4, j4_pos=vrep.simxGetJointPosition(clientID, UR5_joint4_Handle, vrep.simx_opmode_buffer)rc5, j5_pos=vrep.simxGetJointPosition(clientID, UR5_joint5_Handle, vrep.simx_opmode_buffer)rc6, j6_pos=vrep.simxGetJointPosition(clientID, UR5_joint6_Handle, vrep.simx_opmode_buffer)print("j1_pos:", j1_pos)print("j2_pos:", j2_pos)print("j3_pos:", j3_pos)print("j4_pos:", j4_pos)print("j5_pos:", j5_pos)print("j6_pos:", j6_pos)print("-----------------------")time.sleep(0.2)breakelse:print("waiting for server response...")time.sleep(0.001) # 0.001是我手调出来的,便于演示而已# 测试服务端是否继续在发送数据给客户端,以第一个关节为例
time.sleep(3)
if vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_buffer)[0]==vrep.simx_return_ok:print("客户端待机3秒后,服务端依然在发送数据。")print("关节1的角度为",vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_buffer)[1])elif vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_buffer)[0]==vrep.simx_return_novalue_flag:print("客户端待机3秒后,服务端已停止发送数据。")# 强制擦除存放在服务端的指令,再测试服务端是否还在发送数据
print('擦除存放在服务端的指令...')
while True:# 因为客户端到服务端的指令是有延迟的,所以需要这个While循环来确保确实已经擦除服务端的命令,实际使用时不必这样测试。# 另外这里面的逻辑需要注意一下,第一次检测到vrep.simx_return_novalue_flag时,应该是While循环第一个指令造成的,而不是# 当前的那个rc1, j1_pos=vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_discontinue)if rc1==vrep.simx_return_ok:print("waiting for server response...")time.sleep(0.001) # 0.001是我手调出来的,便于演示而已elif rc1==vrep.simx_return_novalue_flag:print("server responds!")breakif vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_buffer)[0]==vrep.simx_return_ok:print("强制擦除后,服务端依然在发送数据。")print("关节1的角度为",vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_buffer)[1])
elif vrep.simxGetJointPosition(clientID, UR5_joint1_Handle, vrep.simx_opmode_buffer)[0]==vrep.simx_return_novalue_flag:print("强制擦除后,服务端已停止发送数据。")
Connected to remote API server
waiting for server response...
waiting for server response...
waiting for server response...
waiting for server response...
waiting for server response...
waiting for server response...
j1_pos: 0.04933595657348633
j2_pos: 0.04936075210571289
j3_pos: -0.049343109130859375
j4_pos: 0.04935884475708008
j5_pos: 0.04934239387512207
j6_pos: 0.049343109130859375
-----------------------
j1_pos: 0.9543983936309814
j2_pos: 0.9548768997192383
j3_pos: -0.955643892288208
j4_pos: 0.9558093547821045
j5_pos: 0.9556386470794678
j6_pos: 0.9556429386138916
-----------------------
j1_pos: 1.5655755996704102
j2_pos: 1.565735101699829
j3_pos: -1.5655508041381836
j4_pos: 1.5706815719604492
j5_pos: 1.5680694580078125
j6_pos: 1.568070411682129
-----------------------
客户端待机3秒后,服务端依然在发送数据。
关节1的角度为 -9.5367431640625e-07
擦除存放在服务端的指令...
waiting for server response...
waiting for server response...
waiting for server response...
waiting for server response...
waiting for server response...
server responds!
强制擦除后,服务端已停止发送数据。
同步模式(Synchronous operation)
以上3种模式在仿真进行时服务端只管往前运行,并不考虑客户端的进度。Remote API在默认情况下是异步运行的。但有时候,我们需要客户端与仿真过程同步——通过远程API控制仿真进度实现。这可以用Remote API的同步模式实现。此时服务端需要提前设置为同步模式。
服务端设置同步模式可以通过以下几种方式实现
simRemoteApi.start
函数- 连续remote API服务端服务配置文件
remoteApiConnections.txt
以下是同步模式的例子
simxSynchronous(clientID,true); // Enable the synchronous mode (Blocking function call)
simxStartSimulation(clientID,simx_opmode_oneshot);// The first simulation step waits for a trigger before being executedsimxSynchronousTrigger(clientID); // Trigger next simulation step (Blocking function call)// The first simulation step is now being executedsimxSynchronousTrigger(clientID); // Trigger next simulation step (Blocking function call)// The second simulation step is now being executed...
下图阐明了同步操作模式:
当调用同步触发器(simxSynchronousTrigger
)时,下一个仿真步开始计算。这并不意味着当函数调用返回时,下一个模拟步骤将完成计算。因此,您必须确保读取正确的数据。如果没有采取特殊措施,则可能从之前的仿真步骤或当前仿真步骤读取数据,如下图所示:
有几种方式来克服以上的问题。
最简单的方法是在调用同步触发器(simxSynchronousTrigger
)后直接以阻塞方式调用函数(其实这里官网想表达的意思是直接调用一个阻塞方式的函数,函数任意,比如simxGetPintTime
,原因嘛,就是让这个阻塞过程来强制占用(这里不好解释了)):
simxSynchronous(clientID,true); // Enable the synchronous mode (Blocking function call)
simxStartSimulation(clientID,simx_opmode_oneshot);// The first simulation step waits for a trigger before being executedsimxSynchronousTrigger(clientID); // Trigger next simulation step (Blocking function call)// The first simulation step is now being executedsimxGetPingTime(clientID); // After this call, the first simulation step is finished (Blocking function call)// Now we can safely read all streamed values
下图说明了上述过程
下图说明了如何在服务器端(即在V-REP远程API插件端)处理初始化的远程API命令:
附加内容
在客户端(即,你的IDE),最少会运行两个线程:①主线程(从中调用Remote API);②通信线程(从中传送数据)。在客户端,可以有任意多的通信线程(通信线):可用simxStart
来启动每一个。在服务器端使用V-REP插件实现,以类似的方式运行。下图说明了Remote API的工作方式:
- simx_opmode_oneshot:非阻塞模式(non-blocking mode)。命令送去服务端执行
(1)-(b)-(3)
。从本地缓冲区返回对先前执行的同一命令的响应(如果有的话(i)-(2)
)。函数不等待从服务端(7)-(i)
的响应。
在服务端,指令被暂存在(4)-(d)
,沿着(d)-(9)-(g)
执行一次,并沿着(g)-(6)
传送响应结果。这个模式常常被“设置类函数(set-functions)”(如simxSetJointPosition
)使用,用户并不关心返回值。 - simx_opmode_blocking:阻塞模式(blocking mode)。命令送去服务端执行
(1)-(b)-(3)
,并等待从服务端返回的响应(7)-(i)-(2)
。然后接收到的响应被从输入箱缓存中删除(i)
,这操作是阻塞模式独有的。
在服务端,指令被暂存在(4)-(d)
,沿着(d)-(9)-(g)
执行一次,并沿着(g)-(6)
传送响应结果。这个模式常常被“得到类函数(get-functions)”(如simxGetObjectHandle
)使用,用户需要得到响应。 - simx_opmode_streaming:非阻塞模式(non-blocking mode)。命令送去服务端执行
(1)-(b)-(3)
。从本地缓冲区返回对先前执行的同一命令的响应(如果有的话(i)-(2)
)。函数不等待从服务端(7)-(i)
的响应。
与simx_opmode_oneshot类似,但是在服务端指令被暂存在(4)-(e)
(而非(4)-(d)
),连续执行(e)-(9)-(g)
,并持续传送回客户端(g)-(6)
。这个模式常常被“得到类函数(set-functions)”(如simxGetJointPosition
)使用,用户经常需要一个特定的值。 - simx_opmode_oneshot_split
- simx_opmode_streaming_split
- simx_opmode_discontinue
- simx_opmode_buffer:非阻塞模式(non-blocking mode)。不向服务端传送指令,但是如果
(i)-(2)
可用,则从本地缓冲区返回对先前执行的相同命令的响应。此模式通常与simx_opmode_streaming或simx_opmode_streaming_split操作模式一起使用:首先,用一个streaming指令启动,然后提取数据。 - simx_opmode_remove
VREP Remote API工作模式详解(未写完,完成度90%)相关推荐
- RabbitMQ工作流程和工作模式详解
RabbitMQ工作流程 生产者发送消息的流程 生产者连接RabbitMQ,建立TCP连接( Connection),开启信道(Channel) 生产者声明一个Exchange(交换器),并设置相关属 ...
- linux apache两种工作模式详解
apache两种工作模式详解 刚接触这两个配置时很迷糊,全部开启或全部注释没有几多变化.今天搜索到这么一篇讲得还不错的文章,看了几篇,还是不能完全记住,做一个收藏. 空闲子进程:是指没有正在处理请求的 ...
- 无线Wifi模块AP和STA工作模式详解
无线Wifi模块AP和STA工作模式详解 Wifi模块包括两种工作模式AP和STA,在这两模式的支持下,无线图传产品可以实现一发多收的功能.下面我们就从AP和STA的基本概念开始了解它们. 一.AP和 ...
- ST MCU_GPIO的八种工作模式详解
GPIO的八种工作模式详解 浮空输入_IN_FLOATING 带上拉输入_IPU 带下拉输入_IPD 模拟输入_AIN 开漏输出_OUT_OD 推挽输出_OUT_PP 开漏复用输出_AF_OD 推挽复 ...
- V90 PN伺服EPOS回零+点动JOG+MDI+程序步具体工作模式详解
V90 PN伺服EPOS回零+点动JOG+MDI+程序步具体工作模式详解 1. 回零 增量式编码器主动回零 在配置回零参数界面,选择回参考点方式: 0:信号REF 即直接设定参考点方式(将当前位置 ...
- LVS工作原理以及三种工作模式详解
1.负载均衡LVS基本介绍 LB集群的架构和原理很简单,就是当用户的请求过来时,会直接分发到Director Server上,然后它把用户的请求根据设置好的调度算法,智能均衡地分发到后端真正服务器(r ...
- Linux下snort的运行模式,Snort工作模式详解
Snort有3种工作模式,分别为嗅探器模式.分组数据包记录模式与网络**检测模式. m 嗅探器模式. Snort使用Libpcap包捕获库.在该模式下,Snort使用网络接口的混杂模式读取并解 ...
- GPIO工作模式详解(含Arduino实例)
1. GPIO介绍 从最基础的51单片机,Arduino,到STM32,树莓派等等,这些上面都会有GPIO口这么一个概念,如果你点开了我这个博客,说明你大概率开始学习单片机,那么你应该了解的就是这些口 ...
- 摘抄--apache工作模式详解
服务器优化配置 Apache的主要优势就是能更好地支持多处理器,在编译时通过使用--with-mpm选项来决定Apache的工作模式.如果知道当前的Apache使用的工作机制,则可以通过httpd - ...
最新文章
- 用c语言描述单链表的数据类型,数据结构—单链表(类C语言描述)
- Android 开发 技术大纲 某学课堂
- PHP页面间参数传递的四种方式
- springboot整合servlet
- RocketMQ源码解析:Filtersrv
- 【284天】我爱刷题系列(43)
- 在浏览器控制台输出内容 console.log(string);
- 这是一个我面试某公司的算法题目:对一个字符数组进行排序,根据给定的字符,大于它的,放在数组的左边,小于它的,放在数组的右边,且数组中的元素之间的相对位置要保持不变。...
- 五个温度带的分界线_亚热带,暖温带,到底是些什么带?
- oracle net conf启动无反应,weblogic突然无法启动,显示Server state changed to FORCE
- C++基础:第五章 表达式基础与详述
- linux zmq编译pgm,czmq交叉编译
- [地图代数]处理DEM中的高程异常值——ArcGIS栅格计算的应用
- 考研数学真题复盘(2013-2016)
- Acer 4750 安装黑苹果_超详细安装黑苹果教程
- 关于网络超时时间那些事
- mysql 数据库update_[数据库]MySQL 常用的UPDATE操作
- 【历史上的今天】3 月 2 日:雅虎正式成立;PC 设计先驱诞生;Excite@Home 破产
- 红帽linux员工数,红帽企业 Linux Atomic Host 管理
- srand c语言,C语言srand()rand()
热门文章
- RNA-seq数据下载
- 国内服务器证书,中国互联网协会-CNNIC推出国内首个服务器域名证书
- UNITY 虚拟相机 Cinemachine 第三人称视角 新输入系统 超简单
- python 窗口置顶_PyQt5 窗口置顶
- html 窗口置顶,通用窗口置上(窗口置顶)工具
- 窗口置顶工具v2.0.0
- 解决Mingw-w64下载太慢问题
- 查看apk的包名和启动页activity,adb命令启动app
- robomongo(robo3T)操作MongoDB数据库常用命令
- 博图安装msi失败_博途V13 安装的时候出现一个 MSI File initialization failed:ERROR_INSTALL_FAILURE 请问是怎么回事啊?...