文章目录
- 前言
- 一、VS Connect 概念引入
- 二、VS Connect 通讯框架
- 三、Carsim 工程配置
- 1、车辆模型配置
- 2、procedure配置
- 3、Run Control配置
- 4、受控车辆名称配置
- 四、VS Connect Server代码
- 1、打开Sln工程
- 2、代码修改
- 五、VS Connect Client代码
- 1、函数的调用关系
- 2、carsim_variable代码
- 3、VsConnect_demo代码
- 六、Vs Connect联合仿真
- 1、Server代码编译
- 2、运行carsim
- 3、运行Server端
- 4、运行Client端
前言
为什么要使用VS Connect:
联合仿真:VS Connect API 允许在多个仿真软件之间进行同步仿真。这意味着可以在同一时间步内,多个仿真程序共享数据和状态信息,从而实现更复杂的联合仿真场景。例如,CarSim 可以与 TruckSim 或 BikeSim 同步运行,以模拟不同类型车辆在同一交通环境中的相互影响。
分布式仿真:VS Connect API 支持分布式仿真,这意味着仿真程序可以运行在不同的计算机上,通过网络( UDP)进行通信和数据交换。这对于需要大量计算资源的仿真场景特别有用,因为可以利用多台计算机的计算能力来分担仿真负载。
实时通信:通过 UDP 协议,VS Connect API 能够实现低延迟的实时数据通信。这对于需要实时交互和快速响应的仿真应用非常重要,例如实时驾驶模拟器、交通仿真系统等。
关键: carsim求解器与VS Connect方法不同的是,carsim求解器调用方法无法在运行的时候加入时间延迟以控制求解器的运行频率,一旦加入时间延迟将无法正常输出动力学信息。
正常情况下,carsim求解器的运行周期大概为0.0005s,显然我们正常的显卡是无法让仿真软件画面的更新频率达到0.0005s,当然你也可以让carsim以0.0005s的运行频率计算动力学信息,然后仿真软件以0.01s或者其他时间去获取动力学信息并更新仿真动画。Vs Connect已经兼容这种方式,并且支持多个通讯服务。
CARLA官方集成CARSIM的方法,其实就是CARSIM给UE专门开发了一个VehicleSim Dynamics插件,原理就是通过Vs Connect实现;
一、VS Connect 概念引入
名称 | 描述 |
---|---|
VscNode | 表示本地通信端点,并包含多个 VscLink。应用程序可以创建任意数量的 VscNode。 |
VscLink | 表示与远程 VS Connect 节点的通信通道。要建立连接,一个 VscLink 需要与本地的 VscNode 对象关联。一个 VscNode 可以关联多个 VscLink。 |
VscField | 通俗的讲就是要用于存放 carsim 变量参数的结构体,如变量名为“IMP_PCON_BK”,属性为“ADD 0.0”。 |
VscSchema | VscField 的有序集合。schema 描述了发送或接收的数据更新的结构。一个 schema 可以关联多个 VscField。 |
VscUpdateDefinition | 描述将发送哪些数据以及何时发送。VscUpdateDefinition 包括一个 VscSchema(将发送什么)和调度参数(何时发送数据)。 |
VscContract | 两个连接节点之间的协议,描述了要发送的数据以及发送时间。VscContract 将 VscUpdateDefinition 与 VscLink 关联。Contract 是节点之间传输数据的完整描述,包括传输方向、数据结构(Schema)和数据发送的时间表。 |
VscUpdateData | 在单一更新时间(仿真时间)内的一包仿真数据。VS Connect 的主要目的是允许应用程序交换 VscUpdateData 实例中包含的数据。VscUpdateData 的实例与 VscContract 及其关联的 VscSchema 相关联,后者描述了更新中包含的数据的结构。 |
二、VS Connect 通讯框架
三、Carsim 工程配置
1、车辆模型配置
随便选择个车辆模型
2、procedure配置
1)开环的节气门开度控制-油门
2)开环的制动主缸压力控制-刹车
3)开环的方向盘角度控制
4)运行条件选择Run forver
3、Run Control配置
1)选择Transfer to Local Windows Directory
2)选择工作目录,即simfile要保存的地方
4、受控车辆名称配置
车辆名称配置:VSC_VEHICLE_NAME Vs Vehicle 0 ,以便我们客户端定义需要控制的车辆
四、VS Connect Server代码
1、打开Sln工程
1、首先下载Visual Studio(我用的是2022和2019),打开Carsim项目工程路径,如:C:\Program Files (x86)\CarSim2022.1_Data\Extensions\Custom_C\wrapper_live_animation
中的wrapper_live_animation.sln
;
2、代码修改
1)为什么要修改工程?
因为Carsim官方的案例只支持从Carsim输出变量,不支持输入变量到Carsim,因此需要增加接收Client发送过来的输入变量;打开vs_connect_live_animation.cpp
代码进行修改
2)修改Build()函数,在创建Node节点的时候增加ReceiveUpdateCallback回调函数;
VscNode node = msVscApi->Node_Create_UDPIP( mListenHost.c_str() // listenAddress
, mListenPort // listenPort
, mMaxConnections // maxConnections
, VSC_TSM_NONE // requiredTsMode
, LinkConnectCallback // linkConnectCallback
, LinkDisconnectCallback // linkdisconnectCallback
, ContractRequestCallback // contractRequestCallback
, ContractCanceledCallback // contractCanceledCallback
, SendUpdateCallback // sendUpdateCallback
, ReceiveUpdateCallback // receiveUpdateCallback
, NULL // pingResultsCallback
, &tempResult // out_result
);
3)修改ContractRequestCallback()函数,增加接收Client端传输过来的变量,在if ( VSC_ROLE_SENDER != VsLiveAnimation::GetVscApi()->Contract_GetLocalRole(contract) )
条件中增加此部分代码;
//外部传入
sLogFunc("Incomming Contract request received... ");
//获取Link链接
const auto node = VsLiveAnimation::GetVscApi()->Link_GetNode(link);
//获取车辆Handle
auto veh = (VSEW_VEHICLE_HANDLE)VsLiveAnimation::GetVscApi()->Node_GetAppData(node);
if (!veh || !vsew_IsOk(veh))
{
sLogFunc("Contract cannot be processed because vehicle is not OK. ");
retRes = VSC_RES_ERROR_UNAVAILABLE;
}
else
{
// Find each solver variable named in the Schema of this contract.
auto schema = VsLiveAnimation::GetVscApi()->Contract_GetSchema(contract);
const auto numFields = VsLiveAnimation::GetVscApi()->Schema_GetNumFields(schema);
retRes = VSC_RES_ERROR_INVALID_DATA; // We'll return this error (rejecting the Contract) if we don't find any solver variables to send.
for (int i = 0; i < numFields; ++i)
{
auto field = VsLiveAnimation::GetVscApi()->Schema_GetField(schema, i);
const auto dataType = VsLiveAnimation::GetVscApi()->Field_GetDataType(field);
const auto sizeInBits = VsLiveAnimation::GetVscApi()->Field_GetElementSizeInBits(field);
const auto numElements = VsLiveAnimation::GetVscApi()->Field_GetNumElements(field);
// We only support fields that are a single 64-bit floating point value. Ignore others:
if (VSC_DATATYPE_FLOAT == dataType
&& 64 == sizeInBits
&& 1 == numElements
)
{
//获取Client发送过来的变量信息
const auto objectName = VsLiveAnimation::GetVscApi()->Field_GetObjectName(field);
const auto propertyName = VsLiveAnimation::GetVscApi()->Field_GetPropertyName(field);
const auto Parameters = VsLiveAnimation::GetVscApi()->Field_GetParams(field);
// 将const char* 转换为std::string
string parametersString(Parameters);
// 使用std::stringstream来提取数字部分
stringstream ss(parametersString);
string command;
double Parameters_double;
// 读取第一个字符串(例如 "ADD")
ss >> command;
// 读取第二个字符串(数字部分)
ss >> Parameters_double;
//添加变量到carsim import
vsew_Import_Add3(veh, propertyName, Parameters_double, FALSE, "REPLACE", VSEW_IVA_DEFAULT, VSEW_IVCO_DEFAULT);
const auto varId = vsew_Import_GetId(veh, propertyName);
if (varId >=0)
{
cout << endl;
cout << "Add CARSIM IMPORT VARIABLE: "<< propertyName << " SUCCESS" << endl;
}
else
{
cout << endl;
cout << "CAN NOT ADD VARIABLE: " << propertyName << ",PLEASE CHECK THE RIGHT FORMAT <ADD 0>" << endl;
}
}
}
retRes = VSC_RES_OK; // Returning "OK" accepts this Contract request.
}
4)增加ReceiveUpdateCallback()函数,用于周期地将变量值传入Carsim(可以在SendUpdateCallback()下方的空白处加入次函数);
VscResult ReceiveUpdateCallback( const VscLink link
, const VscContract contract
, const VscRecord out_data)
{
VscResult retRes = VSC_RES_UNDEFINED;
if (VSC_ROLE_SENDER != VsLiveAnimation::GetVscApi()->Contract_GetLocalRole(contract))
{
const auto node = VsLiveAnimation::GetVscApi()->Link_GetNode(link);
auto veh = (VSEW_VEHICLE_HANDLE)VsLiveAnimation::GetVscApi()->Node_GetAppData(node);
if (!veh || !vsew_IsOk(veh))
{
sLogFunc("Contract cannot be processed because vehicle is not OK. ");
retRes = VSC_RES_ERROR_UNAVAILABLE;
}
else
{
// Find each solver variable named in the Schema of this contract.
auto schema = VsLiveAnimation::GetVscApi()->Contract_GetSchema(contract);
const auto numFields = VsLiveAnimation::GetVscApi()->Schema_GetNumFields(schema);
retRes = VSC_RES_ERROR_INVALID_DATA; // We'll return this error (rejecting the Contract) if we don't find any solver variables to send.
for (int i = 0; i < numFields; ++i)
{
auto field = VsLiveAnimation::GetVscApi()->Schema_GetField(schema, i);
const auto dataType = VsLiveAnimation::GetVscApi()->Field_GetDataType(field);
const auto sizeInBits = VsLiveAnimation::GetVscApi()->Field_GetElementSizeInBits(field);
const auto numElements = VsLiveAnimation::GetVscApi()->Field_GetNumElements(field);
// We only support fields that are a single 64-bit floating point value. Ignore others:
if (VSC_DATATYPE_FLOAT == dataType
&& 64 == sizeInBits
&& 1 == numElements
)
{
const auto propertyName = VsLiveAnimation::GetVscApi()->Field_GetPropertyName(field);
double* fieldData = (double*)VsLiveAnimation::GetVscApi()->Record_GetFieldValue(out_data, i);
//get carsim variable id
const auto varId = vsew_Import_GetId(veh, propertyName);
if (varId >= 0)
{
//set init import value
vsew_Import_SetValue(veh, varId, *fieldData);
}
}
}
retRes = VSC_RES_OK; // Returning "OK" accepts this Contract request.
}
}
return retRes;
}
VS Connect Server就加这部分代码。
五、VS Connect Client代码
1、函数的调用关系
代码已上传资源,carsim vs-connect-client代码
2、carsim_variable代码
1)这里就是用来实现carsim变量定义,carsim输入变量请求,carsim输出变量获取;
2)Field变量定义的规则
incoming
outgoing
import ctypes
from ctypes import *
import VsConnectAPI
vscapi = VsConnectAPI.vsc_dll.VsConnectApi_Get # VsConnectApi_GetDefault
vscapi.argtypes=[ctypes.c_int]
vscapi.restype = ctypes.POINTER(VsConnectAPI.Vsc_API)
api_ver3 = vscapi(3)
api_ver3_contents = api_ver3.contents
def StrToChar(string):
string_utf8 = string.encode('UTF-8')
string_buffer = ctypes.create_string_buffer(string_utf8)
return ctypes.cast(string_buffer, c_char_p)
def ResetData():
global gx
global gy
global gz
global gpitch
global gyaw
global groll
ref_x = ctypes.POINTER(ctypes.c_double)
api_ver3_contents.InvalidateDouble(ref_x(ctypes.c_double(gx)))
ref_y = ctypes.POINTER(ctypes.c_double)
api_ver3_contents.InvalidateDouble(ref_y(ctypes.c_double(gy)))
ref_z = ctypes.POINTER(ctypes.c_double)
api_ver3_contents.InvalidateDouble(ref_z(ctypes.c_double(gz)))
ref_pitch = ctypes.POINTER(ctypes.c_double)
api_ver3_contents.InvalidateDouble(ref_pitch(ctypes.c_double(gpitch)))
ref_yaw = ctypes.POINTER(ctypes.c_double)
api_ver3_contents.InvalidateDouble(ref_yaw(ctypes.c_double(gyaw)))
ref_roll = ctypes.POINTER(ctypes.c_double)
api_ver3_contents.InvalidateDouble(ref_roll(ctypes.c_double(gyaw)))
def SendUpdate(out_data):
# IMP_THROTTLE_ENGINE
fieldData = ctypes.cast(api_ver3_contents.RecordGetFieldValue(out_data, 0), ctypes.POINTER(ctypes.c_double))
fieldData[0] = 0.1
fieldData1 = ctypes.cast(api_ver3_contents.RecordGetFieldValue(out_data, 1), ctypes.POINTER(ctypes.c_double))
fieldData1[0] = 0
def ReceiveUpdate(incomingData):
global gx
global gy
global gz
global gpitch
global gyaw
global groll
gx = ((ctypes.cast(api_ver3_contents.RecordGetFieldValue(incomingData, 0), ctypes.POINTER(ctypes.c_double))).contents).value # return pointer double
gy = ((ctypes.cast(api_ver3_contents.RecordGetFieldValue(incomingData, 1), ctypes.POINTER(ctypes.c_double))).contents).value # return pointer double
gz = ((ctypes.cast(api_ver3_contents.RecordGetFieldValue(incomingData, 2), ctypes.POINTER(ctypes.c_double))).contents).value # return pointer double
gpitch = ((ctypes.cast(api_ver3_contents.RecordGetFieldValue(incomingData, 3), ctypes.POINTER(ctypes.c_double))).contents).value # return pointer double
gyaw = ((ctypes.cast(api_ver3_contents.RecordGetFieldValue(incomingData, 4), ctypes.POINTER(ctypes.c_double))).contents).value # return pointer double
groll = ((ctypes.cast(api_ver3_contents.RecordGetFieldValue(incomingData, 5), ctypes.POINTER(ctypes.c_double))).contents).value # return pointer double
def PrintStatementWhenConnected():
print("Dist x: %.2f" % gx, end=" ") if api_ver3_contents.IsValidDouble(gx) \
else print("Dist x:?")
print("Dist y: %.2f" % gy, end=" ") if api_ver3_contents.IsValidDouble(gy) \
else print("Dist y:?")
print("Dist z: %.2f" % gz, end=" ") if api_ver3_contents.IsValidDouble(gz) \
else print("Dist z:?")
print("pitch: %.2f" % gpitch, end=" ") if api_ver3_contents.IsValidDouble(
gpitch) \
else print("pitch:?")
print("yaw: %.2f" % gyaw) if api_ver3_contents.IsValidDouble(gyaw) \
else print("yaw:?")
print("roll: %.2f" % groll) if api_ver3_contents.IsValidDouble(groll) \
else print("roll:?")
# define variables
gx = 0.0
gy = 0.0
gz = 0.0
gpitch = 0.0
gyaw = 0.0
groll = 0.0
#定义Carsim输出变量
incoming_variables = [
{"objectName": StrToChar("Vs Vehicle 0"), "propertyName": StrToChar("Xo"), "propertyParam": StrToChar("")},
{"objectName": StrToChar("Vs Vehicle 0"), "propertyName": StrToChar("Yo"), "propertyParam": StrToChar("")},
{"objectName": StrToChar("Vs Vehicle 0"), "propertyName": StrToChar("Zo"), "propertyParam": StrToChar("")},
{"objectName": StrToChar("Vs Vehicle 0"), "propertyName": StrToChar("pitch"), "propertyParam": StrToChar("")},
{"objectName": StrToChar("Vs Vehicle 0"), "propertyName": StrToChar("Yaw"), "propertyParam": StrToChar("")},
{"objectName": StrToChar("Vs Vehicle 0"), "propertyName": StrToChar("roll"), "propertyParam": StrToChar("")},
]
#定义Carsim输入变量
outgoing_variables = [
{"objectName": StrToChar("Vs Vehicle 0"), "propertyName": StrToChar("IMP_THROTTLE_ENGINE"), "propertyParam": StrToChar("ADD 0.0")},
{"objectName": StrToChar("Vs Vehicle 0"), "propertyName": StrToChar("IMP_PCON_BK"), "propertyParam": StrToChar("ADD 0.0")}
]
3、VsConnect_demo代码
1)这里就是client端的主代码,具体作用请查看《二、VS Connect 通讯框架》
import sys
import platform
import ctypes
from ctypes import *
import time
import VsConnectAPI
from VsConnectAPI import *
import carsim_variable
current_os = platform.system()
if (current_os == "Linux"):
import termios
import select
import atexit
# save the terminal settings
fd = sys.stdin.fileno()
new_term = termios.tcgetattr(fd)
old_term = termios.tcgetattr(fd)
# new terminal setting unbuffered
new_term[3] = (new_term[3] & ~termios.ICANON & ~termios.ECHO)
# switch to normal terminal
def set_normal_term():
termios.tcsetattr(fd, termios.TCSAFLUSH, old_term)
# switch to unbuffered terminal
def set_curses_term():
termios.tcsetattr(fd, termios.TCSAFLUSH, new_term)
def kbhit():
dr,dw,de = select.select([sys.stdin], [], [], 0)
return dr != []
def getch():
return sys.stdin.read(1).encode('utf-8')
else:
import msvcrt
def kbhit():
return msvcrt.kbhit()
def getch():
return msvcrt.getch()
gWantConnection = True
gQuit = False
if (current_os == "Linux"):
import ctypes.util
pathlib = ctypes.util.find_library("c")
libc = ctypes.cdll.LoadLibrary(pathlib)
vsprintf = libc.vsprintf
else:
vsprintf = ctypes.cdll.msvcrt.vsprintf
def logcallback_fnc(loglevel, node, link, contract, format, argptr):
logPrefix = ""
byteString = create_string_buffer(4096)
realString = ""
if (loglevel == VsConnectAPI.VscLogLevel.VSC_LOG_ERROR.value):
logPrefix = "ERROR"
elif (loglevel == VsConnectAPI.VscLogLevel.VSC_LOG_WARNING.value):
logPrefix = "WARNING"
else:
logPrefix = "Log"
byteString = "VS Connect " + logPrefix
realString += byteString.value.decode("utf-8")
if (node or link or contract):
realString += ","
if (node):
byteString = " N-" + str(hex(ctypes.addressof(node)))
realString += byteString.value.decode("utf-8")
if (link):
byteString = " L-" + str(hex(ctypes.addressof(link)))
realString += byteString.value.decode("utf-8")
if (contract):
byteString = " C-" + str(hex(ctypes.addressof(contract)))
realString += byteString.value.decode("utf-8")
realString += ": "
# python casted format and argptr to ints. recast to c_void_p
vsprintf(byteString, ctypes.cast(format, c_void_p), ctypes.cast(argptr, c_void_p))
realString += byteString.value.decode("utf-8")
print(realString)
return len(realString)
def ProcessKeyboardInput():
if kbhit():
pressedKey = getch()
global gWantConnection
global gQuit
if (pressedKey == b'q') or (pressedKey == b'Q'):
gWantConnection = False
gQuit = True
elif (pressedKey == b'd') or (pressedKey == b'D'):
gWantConnection = False
elif (pressedKey == b'c') or (pressedKey == b'C'):
gWantConnection = True
else:
print("Unknown command.\n")
print("Press C to connect, D to disconnect, Q to quit.\n")
@CFUNCTYPE(ctypes.c_int, ctypes.POINTER(VsConnectAPI.VscLink), ctypes.POINTER(VsConnectAPI.VscContract), ctypes.POINTER(VsConnectAPI.VscRecord))
def sendupdatecallback_fnc(link, contract, out_data):
if __debug__:
pass
# schema = api_ver3_contents.ContractGetSchema(contract) # check this
# field = api_ver3_contents.SchemaGetField(schema, 0)
# assert (1 == api_ver3_contents.SchemaGetNumFields(schema))
# assert (1 == api_ver3_contents.FieldGetDataTypes(field))
# assert (1 == api_ver3_contents.FieldGetNumElements(field))
carsim_variable.SendUpdate(out_data)
return int(VsConnectAPI.VscResult.VSC_RES_OK)
@CFUNCTYPE(ctypes.c_int, ctypes.POINTER(VsConnectAPI.VscLink), ctypes.POINTER(VsConnectAPI.VscContract), ctypes.POINTER(VsConnectAPI.VscRecord))
def receiveupdatecallback_fnc(link, contract, incomingData):
if __debug__:
pass
# schema = api_ver3_contents.ContractGetSchema(contract)
# assert (2 == api_ver3_contents.SchemaGetNumFields(schema))
# assert (1 == api_ver3_contents.FieldGetDataTypes(api_ver3_contents.SchemaGetField(schema, 0)))
# assert (1 == api_ver3_contents.FieldGetNumElements(api_ver3_contents.SchemaGetField(schema, 0)))
# assert (1 == api_ver3_contents.FieldGetDataTypes(api_ver3_contents.SchemaGetField(schema, 1)))
# assert (1 == api_ver3_contents.FieldGetNumElements(api_ver3_contents.SchemaGetField(schema, 1)))
carsim_variable.ReceiveUpdate(incomingData)
return int(VsConnectAPI.VscResult.VSC_RES_OK)
def get_VsConnectApi():
# accessing api_ver3
vscapi = VsConnectAPI.vsc_dll.VsConnectApi_Get # VsConnectApi_GetDefault
vscapi.argtypes = [ctypes.c_int]
vscapi.restype = ctypes.POINTER(VsConnectAPI.Vsc_API)
# initialize the VS connect library.
api_ver3 = vscapi(3)
api_ver3_contents = api_ver3.contents
return api_ver3_contents
def create_link(address, portnum):
address_buf = address.encode('UTF-8')
address_char = ctypes.cast(address_buf, c_char_p)
portnum_int = ctypes.c_int(portnum)
glink = api_ver3_contents.LinkCreateUDPIP(address_char, portnum_int, cast(None, ctypes.POINTER(c_int)))
return glink
def create_SchemaInitField(variables):
Schema = api_ver3_contents.SchemaCreate((len(variables)))
for i, variable in enumerate(variables):
api_ver3_contents.SchemaInitField(Schema, i, 1, 64, 1, variable.get("objectName"), variable.get("propertyName"), variable.get("propertyParam"), 0, 0, 0)
return Schema
def create_IncomingContract(glink, Schema, UpdatePeriod_ms):
to_c_double= ctypes.c_double
UpdatePeriod = to_c_double(UpdatePeriod_ms)
UpdateDef = api_ver3_contents.UpdateDefinitionCreate(Schema, 1, UpdatePeriod) #dfefind update period
api_ver3_contents.LinkCreateIncomingContract(glink, UpdateDef, receiveupdatecallback_fnc, 0, -1, None)
return UpdateDef
def create_OutgoingContract(glink, Schema, UpdatePeriod_ms):
to_c_double= ctypes.c_double
UpdatePeriod = to_c_double(UpdatePeriod_ms)
UpdateDef = api_ver3_contents.UpdateDefinitionCreate(Schema, 1, UpdatePeriod) #dfefind update period
api_ver3_contents.LinkCreateOutgoingContract(glink, UpdateDef, sendupdatecallback_fnc, 0, -1, None)
return UpdateDef
def release_DataHandle(Schema, UpdateDef):
ref_Schema = ctypes.POINTER(ctypes.POINTER(VsConnectAPI.VscSchema))
ref_UpdateDef = ctypes.POINTER(ctypes.POINTER(VsConnectAPI.VscUpdateDefinition))
api_ver3_contents.SchemaHandleRelease(ref_Schema(Schema))
api_ver3_contents.UpdateDefinitionHandleRelease(ref_UpdateDef(UpdateDef))
def sync_LinkConnect(node, glink):
#If it is not synchronized, synchronize it
if ((VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_UNCONNECTED.value == connStat) and gWantConnection):
res = api_ver3_contents.LinkConnectAsync(node, glink)
res_val = api_ver3_contents.DescribeResult(res)
print('Initiating connection: %s' % res_val.decode("utf-8"))
elif (((VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_CONNECTED.value == connStat) and not gWantConnection) or \
(( VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_CONNECTING.value == connStat) and not gWantConnection)):
res = api_ver3_contents.LinkDisconnect(glink)
print("Initiating disconnection: %s", api_ver3_contents.DescribeResult(res))
def run_NodeService(localSimTime):
localSimTimeDouble = ctypes.c_double
localSimTimeInput = localSimTimeDouble(localSimTime)
timeDilationDouble = ctypes.c_double
timeDilation = timeDilationDouble(1.0)
processEventBool = ctypes.c_bool
processEventInput = processEventBool(True)
api_ver3_contents.NodeService(node, localSimTimeInput, timeDilation, processEventInput)
def release_NetWorkHandle(glink, node):
ref_glink = ctypes.POINTER(ctypes.POINTER(VsConnectAPI.VscLink))
ref_node = ctypes.POINTER(ctypes.POINTER(VsConnectAPI.VscNode))
api_ver3_contents.LinkHandleRelease(ref_glink(glink))
api_ver3_contents.NodeHandleRelease(ref_node(node))
api_ver3_contents.ShutDown()
# main start here...
api_ver3_contents = get_VsConnectApi()
# Set logging function
VsConnectAPI.SetLogFuncPtr(logcallback_fnc)
api_ver3_contents.Init()
node = api_ver3_contents.NodeCreateUDPIP( None, #None则自动选择IPv4 ip
0, #0则自动通过GetDefaultListenPort()获取
-1,
0,
cast(None, VscLinkConnectedFunc_t),
cast(None, VscLinkDisconnectedFunc_t),
cast(None, VscProcessContractRequestFunc_t),
cast(None, VscContractCanceledFunc_t),
sendupdatecallback_fnc,
receiveupdatecallback_fnc,
cast(None, VscPingResultFunc_t),
cast(None, ctypes.POINTER(c_int))
)
glink = create_link("127.0.0.1", 4380)
update_period = 50
# this can be loop?
incomingSchema = create_SchemaInitField(carsim_variable.incoming_variables)
incomingUpdateDef = create_IncomingContract(glink, incomingSchema, update_period)
release_DataHandle(incomingSchema, incomingUpdateDef)
# now create outgoing contract
# this can be loop?
outgoingSchema = create_SchemaInitField(carsim_variable.outgoing_variables)
outgoingUpdateDef = create_OutgoingContract(glink, outgoingSchema, update_period)
release_DataHandle(outgoingSchema, outgoingUpdateDef)
print("Press C to connect, D to disconnect, or Q to quit.")
# carsim.ResetData()
timeStep = update_period / 1000
timeStepCount = 0
localSimTime = 0.0
if (current_os == "Linux"):
atexit.register(set_normal_term)
set_curses_term()
# # while statement will wrap the following statements
while (not gQuit):
timeStepCount += 1
connStat = api_ver3_contents.LinkGetConnectionStatus(glink)
#sync link connect
sync_LinkConnect(node, glink)
#运行NodeService,数据收发/回调参数处理
run_NodeService(localSimTime)
if (0 == timeStepCount % 20): # reduce output to every nth iteration
print("%.2f" % localSimTime)
if (connStat == VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_UNCONNECTED.value):
print("Disconnected ")
elif (connStat == VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_CONNECTING.value):
print("Connecting ")
elif (connStat == VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_CONNECTED.value):
# print("Connected. ")
carsim_variable.PrintStatementWhenConnected()
elif (connStat == VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_ERROR.value):
print("ERROR ")
elif (connStat == VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_DISCONNECTING.value):
print("Disconnectcing ")
else:
print("************** ")
# a primative timing mechanism. Should be used with this example only.
time.sleep(timeStep)
localSimTime += timeStep
ProcessKeyboardInput()
print("Program exiting, disconnecting link (if it's connected).")
api_ver3_contents.LinkDisconnect(glink)
while (VsConnectAPI.VscConnectionStatus.VSC_CONNSTAT_UNCONNECTED.value != api_ver3_contents.LinkGetConnectionStatus(glink)):
print(".")
api_ver3_contents.NodeService(node, api_ver3_contents.GetInvalidSimtime(), 1, True)
release_NetWorkHandle(glink, node)
六、Vs Connect联合仿真
1、Server代码编译
编译之后有个wrapper_live_animation_Release_x64.exe
2、运行carsim
3、运行Server端
.\wrapper_live_animation_Release_x64.exe “C:\Program Files (x86)\CarSim2022.1_Data\simfile.sim” -host “127.0.0.1” -port 4380
IP 和端口号都在Client代码中定义了
运行Server之后,显示连接数量为0
4、运行Client端
成功输出我定义的变量,哗啦啦的!
综上,完成Carsim Vs Connect demo开发;