02_同步对象SYNC

文章描述了一个CANopen主站设备如何管理节点状态,从初始化到预操作状态,再到操作状态的过程。主站在预操作状态下启动SDO、紧急报文和SYNC功能。同步定时器的设置和回调机制用于发送同步报文,进而触发PDO传输。当条件满足时,主站发送PDO事件,根据传输类型决定是否发送PDO数据报文。

1.首先进行设置节点的状态,节点自动从初始化切入到预操作状态,如果接收到其他节点的boot up,则主节点则通过SDO来对相应的从节点进行配置,配置完成后,需要主站调用函数,将所有的节点设置到操作状态。

主站根据自身的状态开启不同通信的功能,比如:

/*!                                                                                                
**                                                                                                 
**                                                                                                 
** @param d                                                                                        
** @param newCommunicationState                                                                    
**/  	
void switchCommunicationState(CO_Data* d, s_state_communication *newCommunicationState)
{
#ifdef CO_ENABLE_LSS
	StartOrStop(csLSS,	startLSS(d),	stopLSS(d))
#endif
	StartOrStop(csSDO,	None,		resetSDO(d))
	StartOrStop(csSYNC,	startSYNC(d),		stopSYNC(d))
	StartOrStop(csHeartbeat,	heartbeatInit(d),	heartbeatStop(d))
	StartOrStop(csEmergency,	emergencyInit(d),	emergencyStop(d)) 
	StartOrStop(csPDO,	PDOInit(d),	PDOStop(d))
	StartOrStop(csBoot_Up,	None,	slaveSendBootUp(d))
}

UNS8 setState(CO_Data* d, e_nodeState newState)
{
	if(newState != d->nodeState){
		switch( newState ){
			case Initialisation:
			{
				s_state_communication newCommunicationState = {1, 0, 0, 0, 0, 0, 0};
				d->nodeState = Initialisation;
				switchCommunicationState(d, &newCommunicationState);
				/* call user app init callback now. */
				/* d->initialisation MUST NOT CALL SetState */
				(*d->initialisation)(d);				
			}

			/* Automatic transition - No break statement ! */
			/* Transition from Initialisation to Pre_operational */
			/* is automatic as defined in DS301. */
			/* App don't have to call SetState(d, Pre_operational) */
								
			case Pre_operational:
			{
				
				s_state_communication newCommunicationState = {0, 1, 1, 1, 1, 0, 1};
				d->nodeState = Pre_operational;
				switchCommunicationState(d, &newCommunicationState);
				if (!(*(d->iam_a_slave)))
				{
					masterSendNMTstateChange (d, 0, NMT_Reset_Node);
				}
        		(*d->preOperational)(d);
			}
			break;
								
			case Operational:
			if(d->nodeState == Initialisation) return 0xFF;
			{
				s_state_communication newCommunicationState = {0, 1, 1, 1, 1, 1, 0};
				d->nodeState = Operational;
				newState = Operational;
				switchCommunicationState(d, &newCommunicationState);
				(*d->operational)(d);
			}
			break;
						
			case Stopped:
			if(d->nodeState == Initialisation) return 0xFF;
			{
				s_state_communication newCommunicationState = {0, 0, 0, 0, 1, 0, 1};
				d->nodeState = Stopped;
				newState = Stopped;
				switchCommunicationState(d, &newCommunicationState);
				(*d->stopped)(d);
			}
			break;
			default:
				return 0xFF;

		}/* end switch case */
	
	}
	/* d->nodeState contains the final state */
	/* may not be the requested state */
    return d->nodeState;  
}

其中可以看到主节点设备在预操作状态下,boot up上线,SDO和emergency紧急报文和SYNC功能了,协议栈后续一系列的功能都是基于主站的这个同步发生器的功能实现。

/*!                                                                                                
**                                                                                                 
**                                                                                                 
** @param d                                                                                        
**/ 
void startSYNC(CO_Data* d)
{
	if(d->syncTimer != TIMER_NONE){
		stopSYNC(d);
	}

	RegisterSetODentryCallBack(d, 0x1005, 0, &OnCOB_ID_SyncUpdate);
	RegisterSetODentryCallBack(d, 0x1006, 0, &OnCOB_ID_SyncUpdate);

	if(*d->COB_ID_Sync & 0x40000000ul && *d->Sync_Cycle_Period)
	{
		d->syncTimer = SetAlarm(
				d,
				0 /*No id needed*/,
				&SyncAlarm,
				US_TO_TIMEVAL(*d->Sync_Cycle_Period), 
				US_TO_TIMEVAL(*d->Sync_Cycle_Period));
	}
}

先判断同步发生器是否使能并且同步周期的值不为零,然后根据相应的参数设置一个同步定时器。

同步定时器的定时时间到回调相应的函数:

/*!                                                                                                
**                                                                                                 
**                                                                                                 
** @param d                                                                                        
** @param id                                                                                       
**/   
void SyncAlarm(CO_Data* d, UNS32 id)
{
	sendSYNC(d) ;
}
/*!                                                                                                
**                                                                                                 
**                                                                                                 
** @param d                                                                                        
** @param cob_id                                                                                   
**                                                                                                 
** @return                                                                                         
**/  
UNS8 sendSYNC(CO_Data* d)
{
  UNS8 res;
  res = sendSYNCMessage(d);
  proceedSYNC(d) ; 
  return res ;
}

主站向总线上发送一个同步报文,然后处理同步报文。

/*!                                                                                                
**                                                                                                 
**                                                                                                 
** @param d                                                                                        
** @param m                                                                                        
**                                                                                                 
** @return                                                                                         
**/ 
UNS8 proceedSYNC(CO_Data* d)
{

  UNS8 res;
  
  MSG_WAR(0x3002, "SYNC received. Proceed. ", 0);
  
  (*d->post_sync)(d);

  /* only operational state allows PDO transmission */
  if(! d->CurrentCommunicationState.csPDO) 
    return 0;

  res = _sendPDOevent(d, 1 /*isSyncEvent*/ );
  
  /*Call user app callback*/
  (*d->post_TPDO)(d);
  
  return res;
  
}

执行同步报文的回调函数,判断主站节点是否是操作状态,如果不是就返回,然后发送主站节点的PDO,最后执行发送PDO的回调函数。

下面是发送PDO需要先后执行的函数:

/*!
**
**
** @param d
** @param isSyncEvent
**
** @return
**/

UNS8
_sendPDOevent (CO_Data * d, UNS8 isSyncEvent)
{
  UNS8 pdoNum = 0x00;           /* number of the actual processed pdo-nr. */
  UNS8 *pTransmissionType = NULL;
  UNS8 status = state3;
  UNS16 offsetObjdict = d->firstIndex->PDO_TRS;
  UNS16 offsetObjdictMap = d->firstIndex->PDO_TRS_MAP;
  UNS16 lastIndex = d->lastIndex->PDO_TRS;

  if (!d->CurrentCommunicationState.csPDO)
    {
      return 0;
    }


  /* study all PDO stored in the objects dictionary */
  if (offsetObjdict)
    {
      Message pdo;/* = Message_Initializer;*/
      memset(&pdo, 0, sizeof(pdo));
      while (offsetObjdict <= lastIndex)
        {
          switch (status)
            {
            case state3:
              if ( /* bSubCount always 5 with objdictedit -> check disabled */
                   /*d->objdict[offsetObjdict].bSubCount < 5 ||*/
                   /* check if TPDO is not valid */
                   *(UNS32 *) d->objdict[offsetObjdict].pSubindex[1].
                   pObject & 0x80000000)
                {
                  MSG_WAR (0x3960, "Not a valid PDO ", 0x1800 + pdoNum);
                  /*Go next TPDO */
                  status = state11;
                  break;
                }
              /* get the PDO transmission type */
              pTransmissionType =
                (UNS8 *) d->objdict[offsetObjdict].pSubindex[2].pObject;
              MSG_WAR (0x3962, "Reading PDO at index : ", 0x1800 + pdoNum);

              /* check if transmission type is SYNCRONOUS */
              /* message transmited every n SYNC with n=TransmissionType */
              if (isSyncEvent &&
                  (*pTransmissionType >= TRANS_SYNC_MIN) &&
                  (*pTransmissionType <= TRANS_SYNC_MAX) &&
                  (++d->PDO_status[pdoNum].transmit_type_parameter ==
                   *pTransmissionType))
                {
                  /*Reset count of SYNC */
                  d->PDO_status[pdoNum].transmit_type_parameter = 0;
                  MSG_WAR (0x3964, "  PDO is on SYNCHRO. Trans type : ",
                           *pTransmissionType);
                  memset(&pdo, 0, sizeof(pdo));
                  /*{
                    Message msg_init = Message_Initializer;
                    pdo = msg_init;
                  }*/
                  if (buildPDO (d, pdoNum, &pdo))
                    {
                      MSG_ERR (0x1906, " Couldn't build TPDO number : ",
                               pdoNum);
                      status = state11;
                      break;
                    }
                  status = state5;
                  /* If transmission RTR, with data sampled on SYNC */
                }
              else if (isSyncEvent && (*pTransmissionType == TRANS_RTR_SYNC))
                {
                  if (buildPDO
                      (d, pdoNum, &d->PDO_status[pdoNum].last_message))
                    {
                      MSG_ERR (0x1966, " Couldn't build TPDO number : ",
                               pdoNum);
                      d->PDO_status[pdoNum].transmit_type_parameter &=
                        ~PDO_RTR_SYNC_READY;
                    }
                  else
                    {
                      d->PDO_status[pdoNum].transmit_type_parameter |=
                        PDO_RTR_SYNC_READY;
                    }
                  status = state11;
                  break;
                  /* If transmission on Event and not inhibited, check for changes */
                }
              else
                if ( (isSyncEvent && (*pTransmissionType == TRANS_SYNC_ACYCLIC))
                     ||
                     (!isSyncEvent && (*pTransmissionType == TRANS_EVENT_PROFILE || *pTransmissionType == TRANS_EVENT_SPECIFIC)
                       && !(d->PDO_status[pdoNum].transmit_type_parameter & PDO_INHIBITED)))
                {
                  sendOnePDOevent(d, pdoNum);
                  status = state11;
                }
              else
                {
                  MSG_WAR (0x306C,
                           "  PDO is not on EVENT or synchro or not at this SYNC. Trans type : ",
                           *pTransmissionType);
                  status = state11;
                }
              break;
            case state5:       /*Send the pdo */
              sendPdo(d, pdoNum, &pdo);
              status = state11;
              break;
            case state11:      /*Go to next TPDO */
              pdoNum++;
              offsetObjdict++;
              offsetObjdictMap++;
              MSG_WAR (0x3970, "next pdo index : ", pdoNum);
              status = state3;
              break;

            default:
              MSG_ERR (0x1972, "Unknown state has been reached : %d", status);
              return 0xFF;
            }                   /* end switch case */

        }                       /* end while */
    }
  return 0;
}
UNS8
sendOnePDOevent (CO_Data * d, UNS8 pdoNum)
{
  UNS16 offsetObjdict;
  Message pdo;
  if (!d->CurrentCommunicationState.csPDO ||
      (d->PDO_status[pdoNum].transmit_type_parameter & PDO_INHIBITED))
    {
      return 0;
    }

  offsetObjdict = (UNS16) (d->firstIndex->PDO_TRS + pdoNum);

  MSG_WAR (0x3968, "  PDO is on EVENT. Trans type : ",
           *((UNS8 *) d->objdict[offsetObjdict].pSubindex[2].pObject));
  
  memset(&pdo, 0, sizeof(pdo));
  if (buildPDO (d, pdoNum, &pdo))
    {
      MSG_ERR (0x3907, " Couldn't build TPDO number : ",
               pdoNum);
      return 0;
    }

  /*Compare new and old PDO */
  if (d->PDO_status[pdoNum].last_message.cob_id == pdo.cob_id
      && d->PDO_status[pdoNum].last_message.len == pdo.len
      && memcmp(d->PDO_status[pdoNum].last_message.data,
					pdo.data, 8) == 0
    )
    {
      /* No changes -> go to next pdo */
      return 0;
    }
  else
    {

      TIMEVAL EventTimerDuration;
      TIMEVAL InhibitTimerDuration;

      MSG_WAR (0x306A, "Changes TPDO number : ", pdoNum);
      /* Changes detected -> transmit message */
      EventTimerDuration =
        *(UNS16 *) d->objdict[offsetObjdict].pSubindex[5].
        pObject;
      InhibitTimerDuration =
        *(UNS16 *) d->objdict[offsetObjdict].pSubindex[3].
        pObject;

      /* Start both event_timer and inhibit_timer */
      if (EventTimerDuration)
        {
          DelAlarm (d->PDO_status[pdoNum].event_timer);
          d->PDO_status[pdoNum].event_timer =
            SetAlarm (d, pdoNum, &PDOEventTimerAlarm,
                      MS_TO_TIMEVAL (EventTimerDuration), 0);
        }

      if (InhibitTimerDuration)
        {
          DelAlarm (d->PDO_status[pdoNum].inhibit_timer);
          d->PDO_status[pdoNum].inhibit_timer =
            SetAlarm (d, pdoNum, &PDOInhibitTimerAlarm,
                      US_TO_TIMEVAL (InhibitTimerDuration *
                                     100), 0);
          /* and inhibit TPDO */
          d->PDO_status[pdoNum].transmit_type_parameter |=
            PDO_INHIBITED;
        }

      sendPdo(d, pdoNum, &pdo);
    }
    return 1;
}
static void sendPdo(CO_Data * d, UNS32 pdoNum, Message * pdo)
{
  /*store_as_last_message */
  d->PDO_status[pdoNum].last_message = *pdo;
  MSG_WAR (0x396D, "sendPDO cobId :", UNS16_LE(pdo->cob_id));
  MSG_WAR (0x396E, "     Nb octets  : ", pdo->len);

  canSend (d->canHandle, pdo);
}

最后调用底层的函数将PDO的数据发送出去。

<think>我们逐行解释这段代码。这段代码是Python语言,使用了APScheduler库(一个Python的定时任务库)来添加一个定时任务,并根据条件决定是否暂停这个任务。 首先,我们看第一段: ```python scheduler.add_job( run_tushare_historical_sync, CronTrigger.from_crontab(settings.TUSHARE_HISTORICAL_SYNC_CRON, timezone=settings.TIMEZONE), id="tushare_historical_sync", name="历史数据同步(Tushare)", kwargs={"incremental": True} ) ``` - `scheduler.add_job()`: 这是向调度器添加一个任务的方法。 - `run_tushare_historical_sync`: 这是要执行的函数,即任务的具体内容。这个函数应该是在其他地方定义的,用于同步Tushare的历史数据。 - `CronTrigger.from_crontab(...)`: 这里使用CronTrigger来设置任务的触发时间。`from_crontab`方法将一个crontab格式的字符串(存储在`settings.TUSHARE_HISTORICAL_SYNC_CRON`中)转换为触发器,同时指定时区(`settings.TIMEZONE`)。 - `id="tushare_historical_sync"`: 为这个任务指定一个唯一的ID,方便后续引用(如暂停、重启等)。 - `name="历史数据同步(Tushare)"`: 任务的描述性名称。 - `kwargs={"incremental": True}`: 传递给任务函数的关键字参数。这里表示以增量模式运行同步(`incremental=True`)。 接下来是条件判断: ```python if not (settings.TUSHARE_UNIFIED_ENABLED and settings.TUSHARE_HISTORICAL_SYNC_ENABLED): scheduler.pause_job("tushare_historical_sync") logger.info(f"⏸️ Tushare历史数据同步已添加但暂停: {settings.TUSHARE_HISTORICAL_SYNC_CRON}") else: logger.info(f"📊 Tushare历史数据同步已配置: {settings.TUSHARE_HISTORICAL_SYNC_CRON}") ``` - 条件判断:如果`settings.TUSHARE_UNIFIED_ENABLED`和`settings.TUSHARE_HISTORICAL_SYNC_ENABLED`这两个设置项不是同时为True(即其中一个为False或都为False),则执行if块内的代码。 - `scheduler.pause_job("tushare_historical_sync")`: 通过任务ID暂停这个刚刚添加的任务。这样任务就不会按照cron表达式执行,直到被重新启用。 - 然后记录一条暂停的日志信息,使用暂停符号和cron表达式。 - 否则(即两个设置项都为True),则记录一条任务已配置的日志信息,表示任务会按照cron表达式正常运行。 总结:这段代码的作用是添加一个定时同步任务,但根据配置决定是否立即暂停该任务,并记录相应的日志。 注意:这里假设`scheduler`是一个已经创建并启动的调度器实例,`settings`是包含配置的模块,`logger`是日志记录器。 现在,按照要求,在最后提出相关问题。 §§相关问题§§: 1. 如何使用APScheduler在Python中设置定时任务? 2. CronTrigger的crontab字符串格式是怎样的? 3. 如何动态地启用或暂停APScheduler中的任务? 4. 在APScheduler中,传递参数给任务函数有哪些方式? </think> 以下是逐行解释你提供的代码: ```python scheduler.add_job( run_tushare_historical_sync, # 要执行的函数:同步Tushare历史数据的函数 CronTrigger.from_crontab( # 使用CronTrigger创建定时触发器 settings.TUSHARE_HISTORICAL_SYNC_CRON, # 从配置中获取cron表达式 timezone=settings.TIMEZONE # 设置任务执行的时区 ), id="tushare_historical_sync", # 任务唯一标识符 name="历史数据同步(Tushare)", # 任务名称 kwargs={"incremental": True} # 传递给函数的参数(增量同步模式) ) ``` ```python # 检查是否启用Tushare统一服务且启用了历史数据同步 if not (settings.TUSHARE_UNIFIED_ENABLED and settings.TUSHARE_HISTORICAL_SYNC_ENABLED): # 如果未同时满足两个条件,暂停任务 scheduler.pause_job("tushare_historical_sync") # 记录暂停日志(带暂停图标) logger.info(f"⏸️ Tushare历史数据同步已添加但暂停: {settings.TUSHARE_HISTORICAL_SYNC_CRON}") else: # 如果两个条件都满足,记录配置成功的日志(带图表图标) logger.info(f"📊 Tushare历史数据同步已配置: {settings.TUSHARE_HISTORICAL_SYNC_CRON}") ``` ### 详细解释: 1. **任务添加**: - `run_tushare_historical_sync`: 实际执行数据同步的核心函数 - `CronTrigger.from_crontab()`: 将字符串格式的cron表达式转换为触发器对象 - `kwargs={"incremental": True}`: 以增量模式运行同步(只同步新数据而非全量) 2. **条件检查**: - 检查两个配置项是否同时为 `True`: - `TUSHARE_UNIFIED_ENABLED`: 是否启用Tushare服务 - `TUSHARE_HISTORICAL_SYNC_ENABLED`: 是否启用历史数据同步功能 - 如果任一条件不满足,则暂停任务 3. **任务管理**: - `pause_job("tushare_historical_sync")`: 通过ID暂停特定任务 - 日志使用Unicode符号(⏸️/📊)增强可读性 4. **配置驱动**: - 所有关键参数(CRON表达式、时区、开关)都来自配置(settings) - 支持动态启用/禁用任务而无需修改代码 ### 典型工作流: 1. 添加定时任务(默认启用状态) 2. 根据配置决定是否立即暂停 3. 后续可通过API动态恢复任务: ```python scheduler.resume_job("tushare_historical_sync") ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值