【PtpBasics】- KRTS C++示例精讲(7)
PtpBasics示例讲解
目录
- PtpBasics示例讲解
- 结构说明
- 代码说明
项目打开请查看【BaseFunction精讲】。
结构说明
PtpBasics.cpp:用户应用层源码
- 其余文件说明请查看【BaseFunction精讲】中的结构说明。
ps : 内核层中的数据、结构体需要一字节对齐,需要以MT方式构建
代码说明
/* Copyright (c) 2017-2024 by Kithara Software GmbH. All rights reserved. */
//##############################################################################################################
//
// 文件: PtpBasics.cpp
//
// 模块: PTP 模块, 时钟模块
//
// 描述: 示例应用程序显示如何设置 PTP 时间同步
//
// 创建人: r.gro 2017-09-04
//
//##############################################################################################################
/*=====================================================================*\
| *** 免责声明 *** |
| |
| 本代码仅是示例程序,您可以随意使用,我们不承担任何法律责任! |
| |
\*=====================================================================*/
//##############################################################################################################
//
// 目的:
//
// 本示例应用程序展示了如何使用 PTP 模块设置 PTP 时间同步。
//
// 在示例中,将为主时钟创建一个 PTP 时钟实例。
// 然后,可以添加一个或多个 PTP 端口。
// 最后,将循环显示 PTP 时间,并显示 PTP 端口和时钟的状态变化。
//
// 在通过以太网连接的两台 PC 上运行示例,可以启用自动时间同步功能。
// 可以启用自动时间同步。
//
//##############################################################################################################
#include "../_KitharaSmp/_KitharaSmp.h"
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// 别忘了输入你的序列号(6位客户编号),这是打开驱动程序所必需的。
//
// 如果你使用Demo版本,也可以使用“DEMO”代替。
// 如果你使用Beta版本,也可以使用“BETA”代替。
//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// 如上说所,定义的客户号
const char _pCustomerNumber[] = "DEMO";
//--------------------------------------------------------------------------------------------------------------
// CallBackData 结构的定义,用于与回调共享数据,作为其引用参数。
//--------------------------------------------------------------------------------------------------------------
struct CallBackData {
KSHandle hClock;
KSHandle* phPort;
int portCount;
int running;
};
//--------------------------------------------------------------------------------------------------------------
// PTP事件回调,该文件末尾定义的回调函数的声明。
//--------------------------------------------------------------------------------------------------------------
KSError __stdcall ptpEventCallBack(void* pArgs, void* /*pContext*/);
// 主程序入口
void runSample() {
outputTxt("***** Kithara example program 'PtpBasics' *****");
// 错误码定义,KSError 是 Kithara API 所有函数的返回类型,通过 【KSError】 可以查询接口的返回错误信息。
KSError ksError;
//------------------------------------------------------------------------------------------------------------
// 打开驱动程序的第一步,所有KRTS程序必须进行的操作。
// 只要该函数调用成功后,我们可以使用其他函数。如果打开失败,则无法调用其他函数。
// 此函数接受您的客户编号作为参数,其中包含 Kithara(如果适用可以为“DEMO”或“BETA”)。
//------------------------------------------------------------------------------------------------------------
ksError = KS_openDriver(
_pCustomerNumber); // 客户序列号
if (ksError != KS_OK) {
outputErr(ksError, "KS_openDriver", "Unable to open the driver!");
return;
}
//------------------------------------------------------------------------------------------------------------
// 创建并初始化传给回调的 CallBackData 实例。
//------------------------------------------------------------------------------------------------------------
CallBackData referenceParameter;
referenceParameter.running = 0;
//------------------------------------------------------------------------------------------------------------
// 创建回调以处理 PTP 事件。
//------------------------------------------------------------------------------------------------------------
Handle hCallBack;
ksError = KS_createCallBack(
&hCallBack, // 返回回调句柄
ptpEventCallBack, // 回调函数
&referenceParameter, // 参考参数传递给回调函数
KSF_USER_EXEC, // 在应用层中执行
0); // 优先级值未使用
if (ksError != KS_OK) {
outputErr(ksError, "KS_createCallBack", "Unable to create user callback!");
KS_closeDriver();
return;
}
//------------------------------------------------------------------------------------------------------------
// 为主时钟创建时钟实例。
//------------------------------------------------------------------------------------------------------------
KSPtpClockConfig clockConfig = { 0 };
clockConfig.structSize = sizeof(KSPtpClockConfig);
clockConfig.mode = inputDec("PTP mode (1 = BMCA / 2 = Grandmaster / 3 = Slave-only): ",
KS_PTP_BEST_MASTER_CLOCK_ALGORITHM);
const short int defaultPriority = 128;
clockConfig.priority1 = inputDec("Priority 1 of Best Master Clock Algorithm: ", defaultPriority);
clockConfig.priority2 = inputDec("Priority 2 of Best Master Clock Algorithm: ", defaultPriority);
KSHandle hClock;
ksError = KS_createPtpClock(
&hClock, // 返回 PTP 时钟句柄
KS_INVALID_HANDLE, // 设备句柄(可选)
&clockConfig, // KSPtpClockConfig结构的地址
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK) {
outputErr(ksError, "KS_createPtpClock", "Failed to create PTP clock instance!");
KS_closeDriver();
return;
}
referenceParameter.hClock = hClock;
//------------------------------------------------------------------------------------------------------------
// 安装一个 PTP 处理程序,如果 PTP 主节点发生变化,将发出信号。
//------------------------------------------------------------------------------------------------------------
ksError = KS_installPtpHandler(
hClock, // PTP 句柄
KS_PTP_MASTER_SELECTED, // 事件代码
hCallBack, // 回调句柄
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK) {
outputErr(ksError, "KS_installPtpHandler", "Failed to install PTP handler!");
KS_closeDriver();
return;
}
//------------------------------------------------------------------------------------------------------------
// 添加一个或多个 PTP 端口.
//------------------------------------------------------------------------------------------------------------
int portCount = inputDec("Number of PTP ports: ", 1);
referenceParameter.portCount = portCount;
referenceParameter.phPort = new KSHandle[portCount];
KSHandle* phAdapter = new KSHandle[portCount];
KSHandle* phVlan = new KSHandle[portCount];
KSPtpPortConfig portConfig;
portConfig.structSize = sizeof(KSPtpPortConfig);
portConfig.announceInterval = 1;
portConfig.syncInterval = 0;
portConfig.delayRequestInterval = 0;
KSNetworkAdapterConfig adapterConfig = { 0 };
adapterConfig.structSize = sizeof(KSNetworkAdapterConfig); // 不要忘记初始化结构体大小
adapterConfig.recvPoolLength = 500;
adapterConfig.sendPoolLength = 500;
adapterConfig.configFlags = KS_NETWORK_ACCEPT_ALL | KS_NETWORK_ENABLE_PTP;
KSNetworkVlanConfig vlanConfig;
vlanConfig.structSize = sizeof(KSNetworkVlanConfig);
vlanConfig.priority = 7;
vlanConfig.dropEligible = 0;
vlanConfig.recvPoolLength = 500;
vlanConfig.sendPoolLength = 500;
KSIPConfig ipConfig;
KS_makeIPv4(&ipConfig.subnetMask, 255, 255, 255, 0);
KS_makeIPv4(&ipConfig.gatewayAddress, 192, 168, 0, 1);
outputTxt(" ");
for (int i = 0; i < portCount; ++i) {
outputTxt(" ");
outputDec(i, "Port: ");
outputTxt("PTP port mode (1 = Standard(IEEE 1588) / 2 = gPTP(IEEE 802.1AS) / 3 = gPTP static-slave): ",
false);
portConfig.mode = inputDec("", KS_PTP_STANDARD_MODE);
if (portConfig.mode == KS_PTP_GPTP_MODE || portConfig.mode == KS_PTP_GPTP_STATIC_SLAVE_MODE) {
portConfig.transportLayer = KS_PTP_ETHERNET;
portConfig.delayMechanism = KS_PTP_DELAY_PEER;
}
else {
portConfig.transportLayer = inputDec("PTP transport layer (1 = Ethernet / 2 = IP-UDP): ",
KS_PTP_ETHERNET);
portConfig.delayMechanism = inputDec("PTP delay mechanism (1 = AUTO / 2 = ENDPOINT / 3 = PEER): ",
KS_PTP_DELAY_ENDPOINT);
}
//----------------------------------------------------------------------------------------------------------
// 选择 PTP 端口正在工作的网络适配器。
//----------------------------------------------------------------------------------------------------------
char pDeviceName[256];
outputTxt(" ");
outputTxt("Following network adapters found:");
for (int i = 0; ; ++i) {
// KS_enumDevices()可以用于查询分配给Kithara驱动程序的所有网络适配器的名称。
ksError = KS_enumDevices(
"NET", // 'NET' 代表搜索网络设备
i, // 从0开始的枚举索引号
pDeviceName, // 返回设备名称
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK) {
if (KSERROR_CODE(ksError) != KSERROR_DEVICE_NOT_FOUND)
outputErr(ksError, "KS_enumDevices", "Unable to query network device name!");
if (KSERROR_CODE(ksError) == KSERROR_DEVICE_NOT_FOUND && !i) {
outputTxt("No network adapters found!");
outputTxt(" ");
KS_closeDriver();
return;
}
break;
}
outputDec(i, "", ": ", false);
outputTxt(pDeviceName);
}
outputTxt(" ");
//----------------------------------------------------------------------------------------------------------
// 输入并保存索引号并根据索引和再次获取设备名称
//----------------------------------------------------------------------------------------------------------
int deviceIndex = inputDec("Device number: ", 0);
//----------------------------------------------------------------------------------------------------------
// 通过获取所选设备的名称来验证选择。
//----------------------------------------------------------------------------------------------------------
ksError = KS_enumDevices(
"NET", // 'NET' 代表搜索网络设备
deviceIndex, // 选择的设备索引号
pDeviceName, // 返回设备名称
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK) {
outputErr(ksError, "KS_enumDevices", "Unable to query selected network device name!");
KS_closeDriver();
return;
}
outputTxt("Selected device: ", false);
outputTxt(pDeviceName);
//----------------------------------------------------------------------------------------------------------
// 打开网络适配器。
// 注意:KSNetworkAdapterConfig.configFlags 必须包含 KS_NETWORK_ENABLE_PTP
// 以启用硬件时间戳!
//----------------------------------------------------------------------------------------------------------
ksError = KS_openNetworkAdapter(
&phAdapter[i], // 返回适配器句柄
pDeviceName, // 设备名称
&adapterConfig, // KSNetworkAdapterConfig结构的地址
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK) {
outputErr(ksError, "KS_openNetworkAdapter", "Failed to open adapter!");
KS_closeDriver();
return;
}
int vlanId = inputDec("VLAN-ID (-1 = VLAN not used): ", -1);
phVlan[i] = KS_INVALID_HANDLE;
if (vlanId != -1) {
if (vlanId < 0 || vlanId > 4095) {
outputTxt("VLAN-ID invalid");
KS_closeDriver();
return;
}
vlanConfig.id = vlanId;
ksError = KS_openNetworkVlan(
&phVlan[i], // 返回 VLAN 处理程序句柄
phAdapter[i], // 适配器句柄
&vlanConfig, // KSNetworkVlanConfig结构的地址
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK) {
outputErr(ksError, "KS_openNetworkVlan", "Failed to create vlan!");
KS_closeDriver();
return;
}
}
//----------------------------------------------------------------------------------------------------------
// 如果 PTP 端口通过 IP/UDP 工作,我们必须设置 IP 配置。
//----------------------------------------------------------------------------------------------------------
KSHandle hArgHandle = phVlan[i] != KS_INVALID_HANDLE ? phVlan[i] : phAdapter[i];
if (portConfig.transportLayer == KS_PTP_UDP) {
//--------------------------------------------------------------------------------------------------------
// 从用户那里获取IP地址的低字节。
//--------------------------------------------------------------------------------------------------------
int lowByte = inputDec("IP-address 192.168.0.x: ", 181);
KS_makeIPv4(&ipConfig.localAddress, 192, 168, 0, lowByte);
ksError = KS_execNetworkCommand(
hArgHandle, // 适配器句柄
KS_NETWORK_SET_IP_CONFIG, // 命令:进行网络IP配置
&ipConfig, // 配置信息
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK) {
outputErr(ksError, "KS_execNetworkCommand", "Failed to set IP config!");
KS_closeDriver();
return;
}
}
//----------------------------------------------------------------------------------------------------------
// 将 PTP 端口添加到时钟实例。
//----------------------------------------------------------------------------------------------------------
ksError = KS_addPtpPort(
hClock, // PTP 时钟句柄
&referenceParameter.phPort[i], // 返回 PTP 端口句柄
hArgHandle, // 连接句柄
&portConfig, // 端口配置
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK) {
outputErr(ksError, "KS_addPtpPort", "Failed to add PTP port!");
KS_closeDriver();
return;
}
//----------------------------------------------------------------------------------------------------------
// 安装 PTP 处理程序,当端口状态发生变化时发出信号。
//----------------------------------------------------------------------------------------------------------
ksError = KS_installPtpHandler(
referenceParameter.phPort[i], // PTP 句柄
KS_PTP_PORT_STATE_CHANGED, // 事件代码:ptp 端口状态已更改
hCallBack, // 事件回调
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK) {
outputErr(ksError, "KS_installPtpHandler", "Failed to install PTP handler!");
KS_closeDriver();
return;
}
}
outputTxt(" ");
outputTxt(" ");
outputTxt("Key commands: [L]ock clock, [U]nlock clock, [Q]uit");
outputTxt(" ");
//------------------------------------------------------------------------------------------------------------
// 运行一次回调,打印起始状态。
//------------------------------------------------------------------------------------------------------------
referenceParameter.running = 1;
ksError = KS_execCallBack(
hCallBack, // 回调句柄
NULL, // 指向上下文的指针(未使用)
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK) {
outputErr(ksError, "KS_execCallBack", "Failed to execute callback!");
KS_closeDriver();
return;
}
//------------------------------------------------------------------------------------------------------------
// 这是主循环,用于打印 PTP 时间。
// 按 “L ”键,主时钟将被锁定,以避免绝对时间的跳变。
// 按'U'键解锁。
//------------------------------------------------------------------------------------------------------------
bool isRunning = true;
while (isRunning) {
waitTime(100 * ms);
int64 time;
KS_getClock(
&time, // 返回时钟值
KS_CLOCK_PTP_TIME); // 时钟类型
outputDec(time, "PTP time: ", "\r", false);
if (myKbhit()) {
switch (myGetch()) {
case 'q':
case 'Q':
isRunning = false;
break;
case 'l':
case 'L':
ksError = KS_lockPtpClock(
hClock, // 时钟句柄
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK)
outputErr(ksError, "KS_lockPtpClock", "Failed to lock PTP clock!");
else {
outputTxt(" ");
outputTxt(" ");
outputTxt("Master clock locked.");
outputTxt(" ");
}
break;
case 'u':
case 'U':
ksError = KS_unlockPtpClock(
hClock, // 时钟句柄
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK)
outputErr(ksError, "KS_unlockPtpClock", "Failed to unlock PTP clock!");
else {
outputTxt(" ");
outputTxt(" ");
outputTxt("Master clock unlocked.");
outputTxt(" ");
}
break;
}
}
}
//------------------------------------------------------------------------------------------------------------
// 关闭 PTP 端口。
//------------------------------------------------------------------------------------------------------------
for (int i = 0; i < portCount; ++i) {
ksError = KS_closePtp(
referenceParameter.phPort[i], // 时钟端口句柄
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK)
outputErr(ksError, "KS_closePtp", "Unable to close PTP port!");
}
//------------------------------------------------------------------------------------------------------------
// 关闭PTP时钟.
//------------------------------------------------------------------------------------------------------------
ksError = KS_closePtp(
hClock, // PTP时钟句柄
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK)
outputErr(ksError, "KS_closePtp", "Unable to close PTP clock!");
//------------------------------------------------------------------------------------------------------------
// 关闭网络适配器
//------------------------------------------------------------------------------------------------------------
for (int i = 0; i < portCount; ++i) {
if (phVlan[i] != KS_INVALID_HANDLE) {
ksError = KS_closeNetwork(
phVlan[i], // VLAN网络句柄
KSF_NO_FLAGS); // 无标记
}
if (ksError != KS_OK)
outputErr(ksError, "KS_closeNetwork", "Unable to close vlan network adapter!");
ksError = KS_closeNetwork(
phAdapter[i], // 网络适配器句柄
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK)
outputErr(ksError, "KS_closeNetwork", "Unable to close network adapter!");
}
delete [] referenceParameter.phPort;
delete [] phAdapter;
delete [] phVlan;
//------------------------------------------------------------------------------------------------------------
// 移除回调
//------------------------------------------------------------------------------------------------------------
ksError = KS_removeCallBack(
hCallBack); // 回调句柄
if (ksError != KS_OK)
outputErr(ksError, "KS_removeCallBack", "Failed to remove callback!");
//------------------------------------------------------------------------------------------------------------
// 关闭驱动程序以释放任何分配的资源。
//------------------------------------------------------------------------------------------------------------
ksError = KS_closeDriver();
if (ksError != KS_OK)
outputErr(ksError, "KS_closeDriver", "Unable to close the driver!");
waitTime(500 * ms);
outputTxt(" ");
outputTxt("End of program 'PtpBasics'.");
}
//--------------------------------------------------------------------------------------------------------------
// PTP 事件处理程序的回调函数。
// 查询并显示 PTP 时钟和端口的状态。
//--------------------------------------------------------------------------------------------------------------
KSError __stdcall ptpEventCallBack(void* pArgs, void* /*pContext*/) {
KSError ksError;
CallBackData* pData = (CallBackData*)pArgs;
if (pData->running == 0)
return KS_OK;
KSPtpClockState clockState;
clockState.structSize = sizeof(KSPtpClockState); // 不要忘记初始化结构体大小
KSPtpPortState portState;
portState.structSize = sizeof(KSPtpPortState); // 不要忘记初始化结构体大小
ksError = KS_getPtpClockState(
pData->hClock, // PTP 时钟句柄
&clockState, // 返回时钟状态
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK) {
outputErr(ksError, "KS_getPtpClockState", "Failed to get PTP clock state!");
return ksError;
}
outputTxt(" ");
outputTxt("clockId: ", false);
for (int i = 0; i < 8; ++i)
outputHex02(clockState.clockIdentity.identity[i], "", "", false);
outputTxt(" ");
outputTxt("parentId: ", false);
for (int i = 0; i < 8; ++i)
outputHex02(clockState.parentPortIdentity.identity.identity[i], "", "", false);
outputDec(clockState.parentPortIdentity.portNumber, ":");
outputTxt("grandMasterId: ", false);
for (int i = 0; i < 8; ++i)
outputHex02(clockState.grandMasterIdentity.identity[i], "", "", false);
outputTxt(" ");
outputDec(clockState.stepsFromGrandMaster, "stepsFromGrandMaster: ");
outputDec(clockState.utcOffset, "utcOffset: ");
for (int i = 0; i < pData->portCount; ++i) {
ksError = KS_getPtpPortState(
pData->phPort[i], // PTP 端口句柄
&portState, // 返回端口状态
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK) {
outputErr(ksError, "KS_getPtpPortState", "Failed to get PTP port state!");
return ksError;
}
outputDec(i, "Port ", ": ", false);
outputTxt("state: ", false);
switch(portState.state) {
case 0: outputTxt("NONE"); break;
case KS_PTP_STATE_INITIALIZING: outputTxt("INITIALIZING"); break;
case KS_PTP_STATE_FAULTY: outputTxt("FAULTY"); break;
case KS_PTP_STATE_DISABLED: outputTxt("DISABLED"); break;
case KS_PTP_STATE_LISTENING: outputTxt("LISTENING"); break;
case KS_PTP_STATE_PRE_MASTER: outputTxt("PRE_MASTER"); break;
case KS_PTP_STATE_MASTER: outputTxt("MASTER"); break;
case KS_PTP_STATE_PASSIVE: outputTxt("PASSIVE"); break;
case KS_PTP_STATE_UNCALIBRATED: outputTxt("UNCALIBRATED"); break;
case KS_PTP_STATE_SLAVE: outputTxt("SLAVE"); break;
default: outputTxt("UNKNOWN"); break;
}
if (portState.state == KS_PTP_STATE_FAULTY) {
outputTxt("Faulty state detected - trying to reset...");
ksError = KS_execPtpCommand(
pData->phPort[i], // PTP 句柄
KS_PTP_CLEAR_FAULTS, // 命令: ptp 清除故障
NULL, // 参数
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK)
outputErr(ksError, "KS_execPtpCommand", "Failed to execute reset command!");
}
}
outputTxt(" ");
return KS_OK;
}