参考运动库 *********************************************************************************************** * The MotPGLib is a library of path generations functions. It can either be called from * simulink or hand coded C routines on the motion module. * * $Id: MotPGLib.c,v 1.3 2011/10/12 22:00:42 wlm Exp $ * * **********************************************************************************************/ /*`============================================================================ *` Included Files *`============================================================================*/ #include "MotPGLib.h" /* Main Include file for file */ #include "MOTPGCSolver.h" /* Include complex solvers as needed */ #include "MathConstants.h" /* General math constants .. PI, N1d3 , etc */ /* #define SIMULATION (1)*/ /* UNDEFINE THIS TO GET BEST PERFORMANCE ON A POWERPC and Take out Negative Test Case CMD IDs */ #ifdef SIMULATION #include "rt_nonfinite.h" #else #include "PathGen_rt_nonfinite.h" #endif #include "copyright.h" #include "system.h" /*lint -esym(578, index) Suppress "Declaration of symbol 'index' hides symbol 'index" */ /*lint -esym(578, time) Suppress "Declaration of symbol 'time' hides symbol 'time" */ /*`============================================================================ *` CONSTANT Definitions *`============================================================================*/ /*`============================================================================ *` STRUCTURE Definitions *`============================================================================*/ /*============================================================================ * ENUMERATION Definitions *============================================================================*/ /* Move Modes */ #define PGUNKNOWN (-1) /* Unable to find a move mode FATAL */ #define PGATCMD (1) /* Move at commanded values */ #define PGDECEL (2) /* Move Requires Deceleration to reach end conditions */ #define PGCONSTANT (3) /* Move requires constant velocity to reach end conditions */ #define PGACCEL (4) /* Move requires acceleration to reach end conditions */ #define PGSTOPACCEL (5) /* Move requires a stop followed by an accel to reach end conditions */ #define PGMINMOVE (6) /* Move too small to plan */ #define PGSTOP (7) /* Stop Immediate non positional move required */ /* AccelDecel Nums */ #define PGDECS12 (1) /* Accel/Decel from segment 1 or 2 */ #define PGDECS357 (2) /* Accel/Decel from segment 3, 5 or 7 */ #define PGDECS6 (3) /* Accel/Decel from Segement 6 */ #define PGDECSM1 (4) /* Accel/Decel case where we are outside the segments and are acceling to get back to seg 1 */ #define PGDECDWBH (5) /* Initial conditions such that we need to wait until we get to where we can decel Dont worry be happy */ #define PGDECDONE (6) /* At constant velocity and done */ #define PGDECERR (-2) /* Too many decel cases selected .. error */ #define PGDECUNKNOWN (-1) /* Unknown decel case */ /* NOT USED See Above #define SIMULATION (1) */ /* UNDEFINE THIS TO GET BEST PERFORMANCE ON A POWERPC and Take out Negative Test Case CMD IDs */ #define PGNUMPATHS (15) /* Make equal to the number of KPP paths */ #undef UNUSED_CODE /* Used to remove obsolete or otherwise unused code */ /* * NOTE Structure forroot solvers */ union special { /* uint64_T nan_uint; */ unsigned long long nan_uint; real_T nan_real; } mynan; /*============================================================================ * MACRO Definitions *============================================================================*/ #define SIGN(val) (((val) > 0) ? 1 : ((val) < 0) ? -1 : 0) #ifdef SIMULATION #define PGfabs(x) ( ( (x) < 0.0 ) ? -1.0*(x) : (x) ) #else /* fabs_fast: This function can be used in place of fabs(). * The compiler does not use the fabs instruction. This function directly * uses the assembly instruction and provides a replacement for the C libaray * function, fabs(). The condition register is modified, so "cc" goes in the * 'clobbered' list */ #define PGfabs fabs_fast #endif /*lint -esym(652, round) Suppress "#define of symbol 'round(double)' declared previously at ..." */ #define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) /*`============================================================================ *` Global Variables *`============================================================================*/ real_T FTolNum; /* Test dynamic Floating point tolerances */ real_T FTolComp; /* Floating Point Tolerance Tables */ #define FPTolTableSize 43 /* double NumBin[37]={2<<1,2<<2,2<<3,2<<4,2<<5,2<<6,2<<7,2<<8,2<<9,2<<10,2<<11,2<<12,2<<13,2<<14,2<<15,2<<16,2<<17,2<<18,2<<19,2<<20,2<<21, 2<<22,2<<23,2<<24,2<<25,2<<26,2<<27,2<<28,2<<29,2<<30,2<<31,2<<32,2<<33,2<<34,2<<35,2<<36,2<<37} ; */ static double NumBin[FPTolTableSize]={.015625,.03125, .0625, .125, .25, .5 , 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608.0, 1677721.0, 33554432.0, 67108864.0, 134217728.0, 268435456.0, 536870912.0, 1073741824.0, 2147483648.0, 4294967296.0, 8589934592.0, 17179869184.0, 34359738368.0, 68719476736.0}; /* 2^-(52-exponent) */ /* double Span[37]={1/(2<<51),1/(2<<49),1/(2<<48),1/(2<<47),1/(2<<46),1/(2<<45),1/(2<<44),1/(2<<43),1/(2<<42),1/(2<<41),1/(2<<40),1/(2<<39), 1/(2<<38),1/(2<<37),1/(2<<36),1/(2<<35),1/(2<<34),1/(2<<33),1/(2<<32),1/(2<<31),1/(2<<30),1/(2<<29),1/(2<<28),1/(2<<27), 1/(2<<26),1/(2<<25),1/(2<<24),1/(2<<23),1/(2<<22),1/(2<<21),1/(2<<20),1/(2<<19),1/(2<<18),1/(2<<17),1/(2<<16),1/(2<<15)}; */ static double Span[FPTolTableSize]={3.46944695195361e-018,6.93889390390723e-018,13.8777878078145e-018, 27.7555756156289e-018, 55.5111512312578e-018 ,111.022302462516e-018, 222.044604925031e-018, 444.089209850063e-018, 888.178419700125e-018, 1.77635683940025e-015, 3.55271367880050e-015, 7.10542735760100e-015, 14.2108547152020e-015, 28.4217094304040e-015, 56.8434188608080e-015, 113.686837721616e-015, 227.373675443232e-015, 454.747350886464e-015, 909.494701772928e-015, 1.81898940354586e-012, 3.63797880709171e-012, 7.27595761418343e-012, 14.5519152283669e-012, 29.1038304567337e-012, 58.2076609134674e-012, 116.415321826935e-012, 232.830643653870e-012, 465.661287307739e-012, 931.322574615479e-012, 1.86264514923096e-009, 3.72529029846191e-009, 7.45058059692383e-009, 14.9011611938477e-009, 29.8023223876953e-009, 59.6046447753906e-009, 119.209289550781e-009, 238.418579101563e-009, 476.837158203125e-009, 953.674316406250e-009, 1.90734863281250e-006, 3.81469726562500e-006, 7.62939453125000e-006, 15.2587890625000e-006}; #ifdef NUM11INTERCOOL /* Contains the current sample number for the current command for that path. Used by the speedup * algos to determine when to do accel only calc and then the decel calcs. i.e. first time through * we only do accel calcs .. next time the decel calc for next sample period and save in our memory * .. sample period after that we do the decel calc for the curren next sample period .. This yields * the two decel calcs we need i.e. current sample and next sample to make a deceleration decision * and are ready to make a deceleration decision. */ static real_T CurMovSample[PGNUMPATHS]={0.0}; #endif /*`============================================================================ *` FUNCTION Declarations *`============================================================================*/ static real_T PGSignfast(real_T NumIn); static boolean_T PGAtMaxVel(real_T vi, real_T VMax, real_T ai); static void PGCheckMaxAccelDecelJerk(real_T JerkCmd,boolean_T JerkInPercent,real_T AMax,real_T DMax, real_T VMax,real_T Ai,real_T Vi,real_T Vf,real_T ts,real_T *JerkCmdAccel, real_T *JerkCmdDecel, int8_T *ErrorIDPtr); #ifdef UNUSED_CODE static void PGDetermineSign(real_T Si,real_T vi,real_T ai,const CmdInBus *CmdIn,real_T V3Last, MaxCmdBus *MaxVarPtr); #endif #ifdef UNUSED_CODE static void PGDetermineSignStop(real_T vi,real_T ai,const CmdInBus *CmdIn,real_T V3Last,MaxCmdBus *MaxVarPtr); #endif static boolean_T PGSameSideZero(real_T In1, real_T In2); static boolean_T PGSameSideZeroGT(real_T In1, real_T In2); static void PGCalcTime2SegExtT1T3JerkUUS3(real_T V0,real_T VMax,real_T A0,real_T AMax,real_T Jerk,real_T SGN, real_T J3Prior,real_T *t1Ptr,real_T *t2Ptr,real_T *t3Ptr,real_T *J1Ptr, real_T *J3Ptr, real_T *J3ModPtr, int8_T *ErrorIDPtr); static void PGCalcTime2SegT1T3JerkUUS3(real_T V0,real_T VMax,real_T A0,real_T AMax,real_T Jerk,real_T SGN, real_T J3Prior,real_T *t1Ptr,real_T *t2Ptr,real_T *t3Ptr,real_T *J1Ptr, real_T *J3Ptr,real_T *J3ModPtr,int8_T *ErrorIDPtr); static void PGCalcAccelMoveJ1NEJ3UUS3(real_T ai,real_T vi,real_T AMax,real_T DMax,real_T VMax,real_T JerkCmd, real_T SGN,real_T J3Prior,real_T VMaxPrior,real_T V3Prior,real_T J5,real_T *a1Ptr, real_T *a2Ptr,real_T *a3Ptr,real_T *t1Ptr,real_T *t2Ptr,real_T *t3Ptr, real_T *J1Ptr,real_T *J3Ptr,real_T *J5_OPtr,int8_T *ErrorIDPtr); static boolean_T PGStopped(real_T A0, real_T V0); static void PGCalcAccelMoveJ1EQJ3UUS3(real_T ai,real_T vi,const MaxCmdBus *MaxVarPtr,real_T J3, const SegmentBus *SegmentPtr, const SegmentBus *SegmentPriorPtr, real_T *a1Ptr, real_T *a2Ptr, real_T *a3Ptr, real_T *t1Ptr, real_T *t2Ptr, real_T *t3Ptr, real_T *J1Ptr, real_T *J3Ptr, int8_T *ErrorIDPtr); static void PGCalcAbsTime(real_T *SegTimePtr, real_T time, real_T *SegAbsTimePtr); static void PGPlanAccelCmd(const CmdInBus *CmdInBusPtr,const CmdOutBus *PathStatePtr,SegmentBus *SegmentPriorPtr, real_T timeOldMove, real_T ts, SegmentBus **SegmentPPtr, real_T *timeMovePtr, int8_T *ErrorIDPtr, boolean_T *ErrorFlagPtr, boolean_T *WarningFlagPtr); static boolean_T PGCheckforMinMove(real_T Ai,real_T Vi,real_T Si,real_T Sf,real_T J3,real_T J5, real_T AMax,real_T DMax,real_T VMax, boolean_T NewPlan, real_T ts, int8_T *ErrorMinMoveChkPtr); static int8_T PGDetermineMoveMode(real_T Ai,real_T Vi,real_T Si,real_T Vf,real_T Sf,real_T VMax,real_T AMax,real_T DMax, real_T J3,real_T J5,real_T SGN,real_T ts,boolean_T CmdDone,boolean_T NewPlan, int8_T CmdID,real_T Ji); static void PGCalcEndCondMove(CmdOutBus CmdOutLast, real_T SampleTime, CmdOutBus *CmdOutPtr); static void PGCalcDecelPath(real_T time, SegmentBus *SegmentpPtr,real_T SampleTime,CmdOutBus *CmdOutPtr, int8_T *ErrorID); static boolean_T PGToleranceCheck(real_T x, real_T y, real_T tolerance); static real_T PGCalcPos(real_T t, real_T Pi, real_T Vi, real_T Ai, real_T Jerk); static real_T PGCalcVel(real_T t, real_T Vi, real_T Ai, real_T Jerk); static real_T PGCalcAccel(real_T t, real_T Ai, real_T Jerk); static void PGGenPathTimeCap(real_T time,SegmentBus *SegmentPtr,CmdInBus *CmdBusPtr, boolean_T NewPlan, real_T SampleTime,CmdOutBus *CmdOutLastPtr, int8_T MoveModeOldNum, DecelCalcsBus *PriorDecelCalcsPtr, CmdOutBus **CmdOutPPtr, int8_T *MoveModeNumPtr, SegmentBus **Segment_0PPtr, DecelCalcsBus **CurDecelCalcsPPtr); static void PGAssignJerkSign(real_T Vi, real_T Vf, real_T J3, real_T J5,real_T *J3Ptr, real_T *J5Ptr); static boolean_T PGCalcCmdRejectDis(real_T Vi,real_T Si,real_T S7, real_T Ai,const MaxCmdBus *MaxVarPtr, real_T J5, real_T J3,real_T SGN); static void PGCalcAccelPath(real_T time, const SegmentBus *SegmentPtr,real_T SampleTime, CmdOutBus *CmdOutAccPtr, int8_T *RegPtr); static void PGDecelSeg12UUS3(real_T Vi,real_T Si,real_T S7,real_T Ai,const MaxCmdBus *MaxVarPtr,real_T J3, real_T *t3Ptr,real_T *t5Ptr,real_T *t6Ptr,real_T *t7Ptr,real_T *J5pPtr, real_T *V3Ptr,real_T *errorPtr); static boolean_T PGTestDecelSolution(real_T DMax,real_T A0,real_T V0,real_T VMax); static void PGCompV3toVFDecelNow(real_T Ai,real_T Vi,real_T VF,real_T J3,boolean_T *V3GTVFPtr,real_T *V3cPtr); static int8_T PGDetermineDecelNum(real_T Ai,real_T Vi,real_T Vf,real_T DMax,real_T AMax,real_T VMax,real_T J3); static boolean_T PGSameSideZeroGTEq(real_T In1, real_T In2); static real_T PGCheckJ3Sign(real_T Ai, real_T DMax, real_T J3); static void quarticRoots(real_T c4, real_T c3, real_T c2, real_T c1,real_T c0, real_T roots[4]); static void PGCalcValuesNoSeg3(real_T Vi,real_T Si,real_T S7,real_T Ai,const MaxCmdBus *MaxVarPtr,real_T J5, real_T *t3Ptr, real_T*t5Ptr, real_T *t6Ptr, real_T *t7Ptr, real_T *J5pPtr, real_T *V3Ptr,real_T *errorPtr); static void PGCalcValuesNoSeg3J5EqJ7(real_T Vi,real_T Si,real_T S7,real_T Ai,const MaxCmdBus *MaxVarPtr, real_T J3,real_T J5,real_T *t3Ptr,real_T *t5Ptr,real_T *t6Ptr, real_T *t7Ptr,real_T *J5pPtr,real_T *V3Ptr,real_T *errorPtr); static void PGCalcDecelTimeNoLim(real_T Vi,real_T Si, real_T S7,real_T Ai,MaxCmdBus *MaxVarPtr,real_T J3, real_T J5,real_T ts,real_T *t3Ptr,real_T *t5Ptr,real_T *t6Ptr,real_T *t7Ptr, real_T *J3Ptr, real_T *J5pPtr, real_T *J7pPtr, real_T *V3Ptr, real_T *errorPtr, int8_T *ErrorIDPtr); static void PGCheckBackup(real_T A0,real_T V0,real_T VMax,real_T DMax,real_T SGN,real_T J3,real_T J5n,real_T J5, real_T errorn,real_T J5n1,real_T *t3Ptr,real_T *t5Ptr,real_T *t6Ptr,real_T *t7Ptr, real_T *V3pPtr,real_T *J3pPtr,real_T *J5pPtr,real_T *J7pPtr,boolean_T *BackUpPtr, boolean_T *StopPtr,int8_T *errorPtr); static void PGCalcTimeToDecelJerkUUS3(const CmdOutBus *CmdOutLastPtr,const CmdInBus *CmdBusPtr,MaxCmdBus *MaxVarPtr, real_T J3, real_T J5,real_T ts,boolean_T NewPlan,const CmdOutBus *CmdOutAccPtr, DecelCalcsBus *CurDecelCalcsPtr,real_T *t3Ptr,real_T *t5Ptr,real_T *t6Ptr, real_T *t7Ptr,boolean_T *BackUpPtr,real_T *J3Ptr,real_T *J5Ptr, real_T *J7Ptr,real_T *V3Ptr,boolean_T *StopPtr,DecelCalcsBus *NextDecelCalcsPtr, int8_T *ErrorIDPtr); static void PGCalcAccelVel(real_T *JPtr,const real_T *tPtr,real_T V4,real_T Vi,real_T Ai,real_T *VPtr, real_T *APtr, int8_T *errorPtr); static void PGAccelSeg12UUS3(real_T Vi,real_T Si,real_T S7,real_T Ai,const MaxCmdBus *MaxVarPtr, real_T J3,real_T SGN, real_T *t3Ptr,real_T *t5Ptr,real_T *t6Ptr, real_T *t7Ptr, real_T *J5pPtr,real_T *V3Ptr, real_T *errorPtr); static void PGCalcTime2SegAccelJerkUUS3(real_T Ai,real_T Vi,real_T V7f,real_T Si,real_T S7,real_T SGN, real_T DMax, real_T *t3Ptr,real_T *t5Ptr,real_T *t6Ptr,real_T *t7Ptr, real_T *a5nPtr,real_T *J5pPtr,real_T *errorPtr); static void PGCalcTime3SegAccelJerkUUS3(real_T Ai, real_T Vi, real_T V7f, real_T Si, real_T S7, real_T SGN, real_T DMax, real_T *t5Ptr, real_T *t6Ptr, real_T *t7Ptr, real_T *J5pPtr, real_T *errorPtr); static void PGCalcTime2SegT6T7JerkUUS3(real_T J5,real_T Ai,real_T Vi,real_T V7f,real_T Si,real_T S7, real_T *t3Ptr,real_T *t5Ptr,real_T *t6Ptr,real_T *t7Ptr,real_T *V3Ptr, real_T *J7Ptr,real_T *error); static void PGCalcValuesAccelNoSeg31(real_T Vi,real_T Si, real_T S7,real_T Ai,const MaxCmdBus *MaxVarPtr, real_T J5, real_T SGN,real_T *t3Ptr,real_T *t5Ptr,real_T *t6Ptr, real_T *t7Ptr,real_T *J5pPtr,real_T *V3Ptr,real_T *errorPtr); static void PGCalcTimeToAccelJerkUUS3(const CmdOutBus *CmdOutLastPtr,const CmdInBus *CmdBusPtr,const MaxCmdBus *MaxVarPtr, real_T J3,real_T J5, real_T ts,boolean_T NewPlan,const CmdOutBus *CmdOutAccPtr, const DecelCalcsBus *CurDecelCalcsPtr,real_T *t3Ptr,real_T *t5Ptr,real_T *t6Ptr, real_T *t7Ptr,real_T *J3pPtr,real_T *J5pPtr,real_T *J7pPtr,real_T *V3pPtr, boolean_T *StopPtr,DecelCalcsBus *NextDecelCalcsPtr,boolean_T *CmdRejectPtr, int8_T *ErrorIDPtr); static void PGCalcDecelSegments(const CmdInBus *CmdBusPtr,const CmdOutBus *CmdOutLastPtr,real_T *SegTimepPtr, const real_T *SegJerkpPtr,real_T Reg,real_T time,real_T V3p,real_T VFinal, real_T *PPtr, real_T *VpPtr,real_T *ApPtr,real_T *TPtr,real_T *TAbsPtr, boolean_T *BackUpPtr, int8_T *ErrorIDPtr); static void PGCheckDecelerate(const CmdOutBus *CmdOutLastPtr,const CmdInBus *CmdBusPtr,MaxCmdBus MaxVar, real_T time, const real_T *SegJerkAccPtr, real_T Reg, real_T SampleTime, boolean_T NewPlan, const CmdOutBus *CmdOutAccPtr, DecelCalcsBus *CurDecelCalcsPtr, SegmentBus *SegmentpDecPtr, boolean_T *StopPtr, boolean_T *BackUpPtr, DecelCalcsBus *NextDecelCalcsPtr,int8_T *ErrorIDPtr); |
---|
wxwangyan12 级别: 实习会员 精华主题: 0 发帖数量: 4 个 工控威望: 11 点 下载积分: 214 分 在线时间: 0(小时) 注册时间: 2024-09-02 最后登录: 2024-09-12 查看wxwangyan12的 主题 / 回贴 | /*` Function Definitions **************************************************/ /*` */ /*`************************************************************************* *` Function: mot_pathgen *` *` Description: Invokes the motion engine to do path generation for an *` axis and path. Multiple paths (2) can exist on a single *` axis. They are computed independently with mot_pathgen. *` *` Inputs: axis_index - specifies the axis to process *` path_id - specifies the path_id *` pos_err_limit - Informs the path planner of the current *` state of position error. TRUE results in "pausing" *` the path planner *` *` Outputs: velocity, acceleration, and position values into the *` data structure specified by axis_id and path_id *` *error_id - pointer to an error code to be passed to the *` caller. See fb_errorid.h for error codes. *` *` Returns: scode - indicates a successful or failed operation. *` A SUCCESS value denotes one of multiple states: *` DONE - pathgen has successfully reached the end of its path *` NO_ERROR - pathgen has encountered no errors and has not yet *` completed its path *` WARNING - pathgen has encountered a non-critical situation *` in which it must pass a warning to the FB via *error_id *` ERROR - pathgen has encountered a critical error in which *` it must pass a warning to the FB via *error_id *` *` Valid scodes are further defined and described in *` mot_subsystem.h *` *`**************************************************************************/ scode mot_pathgen(uint16_t axis_index, /*`*/ uint16_t path_id, /*`*/ boolean pos_err_limit, /*`*/ uint16_t *error_id_ptr) /*`*/ { /* $$$WORK_TODO: flm - temp pointer is not needed until we go back to using * pointer swapping rather than data copy...see WORK_TODO below. AxisBus *AxBus_temp_ptr; */ #ifdef BUILD_WITH_PERFORMANCE_DATA_CAPTURE UINT64 ticStart_Mot = gefTB64Get(); #endif bool backup_allowed; sint8 path_num; /*` Initialize status code */ scode status=MOT_SOK_NO_ERROR; /*` Validate error_id pointer */ if (RARELY(error_id_ptr == NULL)) { /*` If NULL, log an internal error with ERR, return failure SCODE */ ERR_log_new_event(EVENTID_SOFTWARE_SYSTEM_ERROR, MAKE_EVENTID_INFO(EVENT_MODID,ERR_ERROR,axis_index), MAKE_EVENTID_DATA_LINENUM_SCODE(MOT_SFAIL_NULL_PTR)); return MOT_SFAIL_NULL_PTR; } #if MOT_PATHGEN_DEBUG /*save new commands to pathgen into the debug log */ if (pathgen_input[axis_index][path_id].exec == true) { pg_cmdlog[pg_cmdlog_index].command = pathgen_input[axis_index][path_id].cmd_in.CmdID; pg_cmdtime[pg_cmdlog_index] = ceil(TB64_TICS_TO_MSECS(TB64_GET_TICS)); pg_axNum[pg_cmdlog_index] = axis_index; pg_cmdlog[pg_cmdlog_index].commanded_position = pathgen_input[axis_index][path_id].cmd_in.Pos; pg_cmdlog[pg_cmdlog_index].commanded_velocity = pathgen_input[axis_index][path_id].cmd_in.Vel; pg_cmdlog[pg_cmdlog_index].commanded_accel = pathgen_input[axis_index][path_id].cmd_in.Accel; pg_cmdlog[pg_cmdlog_index].commanded_decel = pathgen_input[axis_index][path_id].cmd_in.Decel; pg_cmdlog[pg_cmdlog_index].jerk = pathgen_input[axis_index][path_id].cmd_in.Jerk; pg_cmdlog[pg_cmdlog_index].jerk_units = pathgen_input[axis_index][path_id].cmd_in.JerkUnit; pg_cmdlog[pg_cmdlog_index].final_velocity = pathgen_input[axis_index][path_id].cmd_in.FinalVel; pg_cmdlog[pg_cmdlog_index].first_sample_period = pathgen_input[axis_index][path_id].sample_time; pg_cmdlog_index ++; if (pg_cmdlog_index >=NUM_PGCMDS_TO_LOG){ pg_cmdlog_index %= NUM_PGCMDS_TO_LOG; } if (pg_cmdlog_numrecdmoves < NUM_PGCMDS_TO_LOG){ pg_cmdlog_numrecdmoves ++; } } #endif /* Determine whether to allow backups based on direction limits of the axis */ /* Exceptions - The KPP can reject a new command when disallowed backups */ /* prevents it from succeeding. There are cases (e.g. MC_SetPosition) */ /* where this is not acceptable (KPP rejecting the command after act psn */ /* has been set is bad). To ensure that the command succeeds, we tell */ /* KPP to allow backups on the first sample in these scenarios (below): */ /* 1) If forced no backups (by something like a MC_JogAxis), allow backups */ /* on the first sample and thereafter force disallow backups. */ /* 2) Allow backups on the first sample after after executing SetPosition */ /* and thereafter employ the axes' direction limits. */ /* Note that these execptions use the "exec" flag and the fact that it is */ /* only true on the first sample of a new KPP cmd */ if (pathgen_input[axis_index][path_id].force_no_backup) { if (pathgen_input[axis_index][path_id].exec) { backup_allowed = true; /* Allow backups on the first sample of JogAxis */ } else { backup_allowed = false; /* Thereafter, disallow backups */ } } else if ((pathgen_input[axis_index][path_id].cmd_in.CmdID == pg_change_pos) && (pathgen_input[axis_index][path_id].exec)) { backup_allowed = true; /* Allow backups on the first sample of SetPsn */ } else { backup_allowed = (mot_axis_state[axis_index].path_planning[path_id].negative_direction_enabled && mot_axis_state[axis_index].path_planning[path_id].positive_direction_enabled); } /* Generate a unique path number for KPP */ path_num = (axis_index + NUM_SUPPORTED_AXES*path_id); /* Update history log if one is active */ // todo stk init kpp_history log if (RARELY(kpp_HistoryLog[axis_index][path_id] != NULL)) { if (pathgen_input[axis_index][path_id].exec == true) { motion_UTL_addHistoryReal64(kpp_HistoryLog[axis_index][path_id], MOT_HIST_NEW_CMD1, pathgen_input[axis_index][path_id].cmd_in.Pos, pathgen_input[axis_index][path_id].cmd_in.Vel); motion_UTL_addHistoryReal64(kpp_HistoryLog[axis_index][path_id], MOT_HIST_NEW_CMD2, pathgen_input[axis_index][path_id].cmd_in.Accel, pathgen_input[axis_index][path_id].cmd_in.Decel); motion_UTL_addHistoryReal64(kpp_HistoryLog[axis_index][path_id], MOT_HIST_NEW_CMD3, pathgen_input[axis_index][path_id].cmd_in.Jerk, pathgen_input[axis_index][path_id].cmd_in.FinalVel); motion_UTL_addHistory(kpp_HistoryLog[axis_index][path_id], MOT_HIST_NEW_CMD4, (uint32_t)pathgen_input[axis_index][path_id].cmd_in.CmdID, /*lint !e571 Suppress "Suspicious cast" */ (uint32_t)pathgen_input[axis_index][path_id].cmd_in.JerkUnit, (uint32_t)backup_allowed,0); } } /*` Step the pathgenerator one cycle with given inputs */ PGPathGen(&pathgen_input[axis_index][path_id].cmd_in, /*CmdInBus *CmdInBusNewPtr,*/ pathgen_input[axis_index][path_id].sample_time, /*real_T SampleTime,*/ pathgen_input[axis_index][path_id].exec, /* boolean_T NewCmd,*/ pos_err_limit, /* boolean_T PosErrLim,*/ pathgen_input[axis_index][path_id].AxisIn_ptr, /*AxisBus *AxisInPtr,*/ path_num, /* int8_T AxisNum*/ backup_allowed, /* boolean_T BackUpAllowed*/ pathgen_input[axis_index][path_id].AxisOut_ptr,/* AxisBus *AxisOutPtr, */ &pathgen_input[axis_index][path_id].new_plan); /* boolean_T *NewPlanPtr)*/ /* pathgen_input[axis_index][path_id].AxisBus_Even = pathgen_input[axis_index][path_id].AxisBus_Odd;*/ /* Update the path planning info parameter */ SYS_module_data_store.axis[axis_index].path_plan_info.path_plan_info_bits.new_cmd = pathgen_input[axis_index][path_id].exec; if (pathgen_input[axis_index][path_id].exec == true) { /* If the command is a pathgen reset, don't record the command id since the command id is stale */ if(pathgen_input[axis_index][path_id].cmd_in.CmdID != pg_reset){ if (pathgen_input[axis_index][path_id].cmd_in.CmdID == pg_change_pos){ SYS_module_data_store.axis[axis_index].path_plan_info.path_plan_info_bits.command_id = MC_SET_POSITION; }else{ SYS_module_data_store.axis[axis_index].path_plan_info.path_plan_info_bits.command_id = (uint16_t)(mot_axis_state[axis_index].path_planning[path_id].command); } } SYS_module_data_store.axis[axis_index].path_plan_info.path_plan_info_bits.pg_cmd_id = pathgen_input[axis_index][path_id].cmd_in.CmdID & 0x7; /* only 3 bits */ } SYS_module_data_store.axis[axis_index].path_plan_info.path_plan_info_bits.new_plan = pathgen_input[axis_index][path_id].new_plan; SYS_module_data_store.axis[axis_index].path_plan_info.path_plan_info_bits.backing_up = pathgen_input[axis_index][path_id].AxisOut_ptr->BackUpPend; SYS_module_data_store.axis[axis_index].path_plan_info.path_plan_info_bits.pg_error_id= (sint8) 0; /*` Reset the new command flag since we've executed the path gen. If it was a new command, it isn't anymore. */ pathgen_input[axis_index][path_id].exec = false; /*` Reset the sample period time in case it was altered for a delayed start move */ pathgen_input[axis_index][path_id].sample_time = MOT_SAMPLE_PERIOD_IN_SEC; /*` Store output commands into local command store */ pathgen_output[axis_index][path_id].position = pathgen_input[axis_index][path_id].AxisOut_ptr->CmdOut.Pos; pathgen_output[axis_index][path_id].velocity = pathgen_input[axis_index][path_id].AxisOut_ptr->CmdOut.Vel; pathgen_output[axis_index][path_id].acceleration = pathgen_input[axis_index][path_id].AxisOut_ptr->CmdOut.Accel; pathgen_output[axis_index][path_id].jerk = pathgen_input[axis_index][path_id].AxisOut_ptr->CmdOut.Jerk; /*` Check if PathGen has completed its path */ if ((pathgen_input[axis_index][path_id].AxisOut_ptr->CmdOut.Done == true)&&(pathgen_input[axis_index][path_id].AxisOut_ptr->BackUpPend == false)) { SYS_module_data_store.axis[axis_index].path_plan_info.path_plan_info_bits.cmd_done = true; status = MOT_SOK_DONE; }else { SYS_module_data_store.axis[axis_index].path_plan_info.path_plan_info_bits.cmd_done = false; } /*` Check if PathGen had warnings and set proper error_id */ if (pathgen_input[axis_index][path_id].AxisOut_ptr->CmdOut.Warning == true) { uint8_t err_severity = ERR_WARNING; status = MOT_SOK_WARNING; SYS_module_data_store.axis[axis_index].path_plan_info.path_plan_info_bits.pg_error_id = (sint8) pathgen_input[axis_index][path_id].AxisOut_ptr->CmdOut.ErrorID; switch (pathgen_input[axis_index][path_id].AxisOut_ptr->CmdOut.ErrorID){ case KPP_ERR_BackUpRequired: *error_id_ptr = EVENTID_BACKUP_WARNING; break; /* For the following KPP warnings, log an event but do not propagate to the FB */ case KPP_ERR_MaxVelEqFinVelNoSamp: /* 31 */ case KPP_ERR_MinMove4Seg1TsPErr: /* 40 */ case KPP_ERR_MinMove4Seg1TsVErr: /* 41 */ case KPP_ERR_Long_Move_Warning: /* 60 */ case KPP_ERR_AccelJerkLimMaxVel: /* 124 */ /* Warnings that indicate jerk was internally reduced */ case KPP_ERR_DecelJerkLimMaxVel: /* 125 */ case KPP_ERR_MaxJerkAccelOneSample: /* 126 */ case KPP_ERR_MaxJerkDecelOneSample: /* 127 */ *error_id_ptr = EVENTID_NON_ERROR_PMM_EVENT; err_severity = ERR_INFORMATIONAL; /* Log event in event queue, not IO fault table */ status = MOT_SOK_NO_ERROR; /* Don't signal to FSM that we've encountered a warning */ break; default: *error_id_ptr = EVENTID_JERK_WARNING; break; } /*log a warning*/ ERR_log_new_event(*error_id_ptr, MAKE_EVENTID_INFO(EVENT_MODID,err_severity,axis_index), MAKE_EVENTID_DATA_LINENUM_16BITDATA(pathgen_input[axis_index][path_id].AxisOut_ptr->CmdOut.ErrorID)); } /*` Check if PathGen had errors and set proper error_id */ if (pathgen_input[axis_index][path_id].AxisOut_ptr->CmdOut.Error == true) { uint8_t err_severity = ERR_ERROR; status = MOT_SOK_ERROR; SYS_module_data_store.axis[axis_index].path_plan_info.path_plan_info_bits.pg_error_id = (sint8) pathgen_input[axis_index][path_id].AxisOut_ptr->CmdOut.ErrorID; switch (pathgen_input[axis_index][path_id].AxisOut_ptr->CmdOut.ErrorID){ case KPP_ERR_NotDis2Accel2EC: *error_id_ptr = EVENTID_CANNOT_ACCEL_TO_END_COND; break; case KPP_ERR_NotDis2Accel2EC1: *error_id_ptr = EVENTID_CANNOT_REACH_VEL_AT_POS; break; /* KPP Errors that require a normal stop: */ case KPP_ERR_UnknownCmdID: case KPP_ERR_ErrFloatTol: case KPP_ERR_BadCaseTimCapGenPath: case KPP_ERR_NotValidBlendFinalVel: case KPP_ERR_DirNotAllowed: *error_id_ptr = EVENTID_AXIS_NORMAL_STOPPED; break; case KPP_ERR_NoAccelSolnCC3: *error_id_ptr = EVENTID_MOT_JERK_TOO_SMALL; break; case KPP_ERR_MinMoveAccelMGDecel: *error_id_ptr = EVENTID_MOT_ACC_EXCEEDS_DEC; break; case KPP_ERR_OVERMAXACCELTIME: *error_id_ptr = EVENTID_MOT_OVER_MAX_ACCEL_TIME; break; case KPP_ERR_OVERMAXDECELTIME: *error_id_ptr = EVENTID_MOT_OVER_MAX_DECEL_TIME; break; case KPP_ERR_NoBackUpAllowed: /* Note we give special handling to the case where backups are explicitly * * disallowed (e.g. we are Jogging). Ignore backup errors from the KPP. */ if (pathgen_input[axis_index][path_id].force_no_backup){ err_severity = ERR_INFORMATIONAL; } *error_id_ptr = EVENTID_MOT_NO_BACKUP_ALLOWED; break; case KPP_ERR_InitCondBlendNA: *error_id_ptr = EVENTID_MOT_CANT_BLEND_INIT_CONDS; break; default: /*All others - fast stop the axis */ *error_id_ptr = EVENTID_MOT_UNRECOVERABLE_CALCULATION_ERR; break; } /* Log the error. * * To decode the internal KPP error, convert the last 8 bits of the * * event extra data to a 2C signed integer. The error or warning code * * is listed (in decimal) in MotPGLib.h. */ ERR_log_new_event(*error_id_ptr, MAKE_EVENTID_INFO(EVENT_MODID,err_severity,axis_index), MAKE_EVENTID_DATA_LINENUM_16BITDATA(pathgen_input[axis_index][path_id].AxisOut_ptr->CmdOut.ErrorID)); } /* Copy AxisOut to AxisIn */ /* $$$WORK_TODO: flm - swapping the pointers is causing pathgen to produce * bad results. For the moment, we remove the pointer swap and copy the data (below). * The copy costs about 14usec, so when pathgen has been fixed to handle the * pointer swap, put this code back and take out the copy below. AxBus_temp_ptr = pathgen_input[axis_index][path_id].AxisIn_ptr; pathgen_input[axis_index][path_id].AxisIn_ptr = pathgen_input[axis_index][path_id].AxisOut_ptr; pathgen_input[axis_index][path_id].AxisOut_ptr = AxBus_temp_ptr; */ *pathgen_input[axis_index][path_id].AxisIn_ptr = *pathgen_input[axis_index][path_id].AxisOut_ptr; #ifdef BUILD_WITH_PERFORMANCE_DATA_CAPTURE SYS_module_data_store.module_parms.perf_params.p9121_p9125_mot_fsm_pathgen[axis_index].execTimeWords.execTime2 = gefTB64Get() - ticStart_Mot; #endif /*` Return the status of processing of this function */ return status; } |
---|---|
wxwangyan12 级别: 实习会员 精华主题: 0 发帖数量: 4 个 工控威望: 11 点 下载积分: 214 分 在线时间: 0(小时) 注册时间: 2024-09-02 最后登录: 2024-09-12 查看wxwangyan12的 主题 / 回贴 | /**` \file cam_build_profile.c *`************************************************************************************* *` Routines needed to build cam profiles. Building a cam profile consists primarily *` of generating the coefficients needed by CAM_commandGen, but also includes setting *` some initial values of the profile (from MC_CamTableSelect inputs). *` *` ############################################################################## *` # IMPORTANT! The algorithms in this module are documented in "PACMotion: *` # Curve-Fitting Algorithms for Cams" (hereafter CFAC). This file is stored as *` # a supplementay document to the Cam Subsystem design. *` ############################################################################## *` *` $Id: cam_build_profile.c,v 1.2.4.1 2012/04/23 20:36:57 skandl Exp $ *`*************************************************************************************/ /* Unit-testing can be turned on and off here, but release builds will always * have it off */ /*//#ifdef DEBUG_BUILD*/ #undef CAM_BUILD_PROFILE_UT /*//#endif*/ #ifdef CAM_BUILD_PROFILE_UT #include "stdio.h" #endif /*lint +libh(cam_subsystem.h) make lint ignore what it thinks is mutliple inclusion */ /*`============================================================================ *` Included Files *`============================================================================*/ #include <math.h> /*`*/ #include "cmd.h" /*`*/ #include "utl.h" /*`*/ #include "cam_subsystem.h" /*`*/ #include "cam_manager.h" /*`*/ #include "cam_build_profile.h" /*`*/ #include "cam_command_gen.h" /*`*/ #include "fb_errorid.h" /*`*/ #include "module_ids.h" /*`*/ /*============================================================================ * CONSTANT Definitions *============================================================================*/ #define EVENT_MODID CAM_BUILD_PROFILE_MODID /*============================================================================ * MACRO Definitions *============================================================================*/ /*` Size of the scratch arrays. At this time, this number will track the maximum *` number of points that are allowed in cubic splines since they need the most *` space to compute */ #define CAM_TEMP_ARRAYS_SIZE (4096) /*`*/ /*============================================================================ * SCALAR TYPE Definitions *============================================================================*/ /*` Scratch arrays used to do calculations */ static real64 x_tmp[CAM_TEMP_ARRAYS_SIZE]; static real64 y_tmp[CAM_TEMP_ARRAYS_SIZE]; static real64 b_tmp[CAM_TEMP_ARRAYS_SIZE]; static real64 c_tmp[CAM_TEMP_ARRAYS_SIZE]; static real64 d_tmp[CAM_TEMP_ARRAYS_SIZE]; /*============================================================================ * ENUMERATION Definitions *============================================================================*/ /** * boundary - somewhat arbitrarily, the left side is the START and * and the right side is the END */ typedef enum boundary { START = 1, MIDDLE, END } boundary_t; /** * quad_opts - boundary value options for quadratic splines * */ typedef enum quad_opts { QUAD_START_AUTO, QUAD_START_1STDER, QUAD_START_2NDDER, QUAD_END_AUTO, QUAD_END_1STDER, QUAD_END_2NDDER, QUAD_PERIODIC } quad_opts_t; /** * cubic_opts - boundary value options for cubic splines * */ typedef enum cubic_opts { CUBIC_INVALID, /* not a valid selection */ CUBIC_AUTO_AUTO, CUBIC_AUTO_1STDER, CUBIC_AUTO_2NDDER, CUBIC_1STDER_AUTO, CUBIC_1STDER_1STDER, CUBIC_1STDER_2NDDER, CUBIC_2NDDER_AUTO, CUBIC_2NDDER_1STDER, CUBIC_2NDDER_2NDDER, CUBIC_PERIODIC } cubic_opts_t; /*============================================================================ * STRUCTURE Definitions *============================================================================*/ /* None */ /*`============================================================================ *` FUNCTION Declarations *`============================================================================*/ /*` spline2build: Builds all quadratic sectors in a profile */ STATIC void spline2build(profile_t *profilePtr, uint32_t perm); /*` spline2OneSector: Builds the special case of one quadratic sector */ STATIC scode spline2OneSector(profile_t *profilePtr, sector_t *secPtr, bool periodic); /*` spline2MultiSector: Builds multiple quadratic sectors */ STATIC scode spline2MultiSector(profile_t *profilePtr, uint32_t perm, \ uint32_t firstSectorNum, uint32_t lastSectorNum, \ uint32_t size, bool periodic); /*` spline2CrossBound: [Only periodic profiles] Builds quadratic sectors *` when they wrap around the boundary. */ STATIC scode spline2CrossBound(profile_t *profilePtr, uint32_t perm, \ uint32_t highSectorNum, uint32_t lowSectorNum, \ uint32_t highSize, uint32_t lowSize); /*` spline3build: Builds all cubic sectors in a profile */ STATIC void spline3build(profile_t *profilePtr, uint32_t perm); /*` spline3OneSector: Builds the special case of one cubic sector */ STATIC scode spline3OneSector(profile_t *profilePtr, sector_t *secPtr, bool periodic); /*` spline3MultiSector: Builds multiple cubic sectors */ STATIC scode spline3MultiSector(profile_t *profilePtr, uint32_t perm, \ uint32_t firstSectorNum, uint32_t lastSectorNum, \ uint32_t size, bool periodic); /*` spline3CrossBound: [Only periodic profiles] Builds cubic sectors *` when they wrap around the boundary. */ STATIC scode spline3CrossBound(profile_t *profilePtr, uint32_t perm, \ uint32_t highSectorNum, uint32_t lowSectorNum, \ uint32_t highSize, uint32_t lowSize); /*` setCubeBoundaryOption: sets boundaries for non-peridoic profiles that have *` cubic FIRST AND LAST sectors. */ STATIC cubic_opts_t setCubeBoundaryOption(profile_t *profilePtr); /*` Provides a boundary value when none has been given. */ STATIC real64 autoSetDer(real64 *x,real64 *y,uint32_t n, boundary_t side); /*`*/ /*` Generates coefficents for a linear curve fit. */ STATIC void linear(real64 *x, real64 *y, uint32_t n, real64 *b); /*`*/ /*` Generates coefficents for a Quadratic Spline curve fit. */ STATIC scode spline2(real64 *x, real64 *y, real64 bound, quad_opts_t opts, int32_t n, real64 *b, real64 *c); /*`*/ /*` spline3: This function generates coefficents for a Cubic Spline curve fit. */ STATIC scode spline3(real64 *x, real64 *y, real64 strt_bound, real64 end_bound, cubic_opts_t opts, int32_t n, real64 *b, real64 *c, real64 *d); /*` spline5: This function generates coefficents for a Quintic Spline curve fit. */ STATIC void spline5(real64 *x,real64 *y, uint32_t n, real64 *b, real64 *c, real64 *d, real64 *e, real64 *f); /*============================================================================ * VARIABLE Definitions *============================================================================*/ /* None */ /*`============================================================================ *` FUNCTION Definitions *`============================================================================*/ /** *`*************************************************************************** *` camProfileBuild: Builds the specified permutation of the given profile. Fills *` in the coefficients for the permutation. The cam file must *` have been unpacked into the profile structure and the sectors *` and coefficient pointers must already be set up. Any maximum *` number of sectors or points must be checked elsewhere, as this *` function will try to build any profile it's given. *` *` \param[in,out] profilePtr *` Contains the profile and instance with data from the unpacked *` cam file. *` *` \param in permutation *` Indicates the permutation of the profile to build. This *` permutation MUST be in the BUILDING state, or no coefficents *` will be created. *` *` \return scode *` *` Go through the profile instance by curve-fit types. *` - First, generate coefficients for completly specified sectors *` (i.e. invarient end-conditions) linear and quintic-splines with 1st *` and 2nd derivatives specified. *` - Second, generate coefficients for quadratic-splines (1 free end-condition). *` - Finally, generate coefficients for cubic-splines (2 free end-conditions). *` - Indicate that the profile has been built to CMD and internally *`**************************************************************************/ scode camProfileBuild(profile_t *profilePtr, uint8_t permutation) /*`*/ { scode status = CAM_SOK; uint32_t i, ofst; profileInstance_t *piPtr; /* Pointer to the instance to build coefs for */ real64 *xPtr, *yPtr; sector_t *sPtr; /*` IF NULL input */ if( NULL == profilePtr) { /*` Log Error */ status = CAM_NULL_INPUT; ERR_log_new_event(EVENTID_INTERNAL_SW_STATUS_ERROR, MAKE_EVENTID_INFO(EVENT_MODID,ERR_ERROR,MODULE_INDEX), MAKE_EVENTID_DATA_LINENUM_SCODE(status)); } /*` ELSE IF permutation is too big */ else if (MAX_INST_PERMS <= permutation) { status = CAM_FATAL_ERROR; ERR_log_new_event(EVENTID_INTERNAL_SW_STATUS_ERROR, MAKE_EVENTID_INFO(EVENT_MODID,ERR_ERROR,MODULE_INDEX), MAKE_EVENTID_DATA_LINENUM_SCODE(permutation)); } /*` ELSE */ else { /*` Set up some local pointers */ piPtr = profilePtr->camInstArr[permutation]; xPtr = profilePtr->x; yPtr = profilePtr->y; /*` IF profile instance pointer is not NULL */ if(piPtr != NULL) { /*` IF profile instance state is not built */ if(NOT_BUILT == piPtr->state) { /*`*********************************************************** *` Linear and quitic sectors are complete (i.e. they do not *` have user-definable boundary conditions), which is why *` they can be built without the additional functions that *` the quadratic and cubic functions have. *`***********************************************************/ /*` Build Linear Sectors */ for(i=0; i < profilePtr->numOfSectors; i++) { if(piPtr->secPtr.curve_fit_type == LINEAR_FIT) { ofst = piPtr->secPtr.table_location; sPtr = &(piPtr->secPtr); /* If this is not the last sector, leave the size alone * Else, this is the last sector, subtract one from the * size. */ if (i != profilePtr->numOfSectors-1) { linear(&xPtr[ofst],&yPtr[ofst],sPtr->size,sPtr->b); } else { linear(&xPtr[ofst],&yPtr[ofst],sPtr->size-1,sPtr->b); } } } /*` Build Quintic Sectors (of deficiency 3) */ for (i=0; i < profilePtr->numOfSectors; i++) { if (piPtr->secPtr.curve_fit_type == QUINTIC_SPLINE) { ofst = piPtr->secPtr.table_location; sPtr = &(piPtr->secPtr); /* If this is not the last sector, leave the size alone * Else, this is the last sector, subtract one from the * size. */ if (i != profilePtr->numOfSectors-1) { spline5(&xPtr[ofst], &yPtr[ofst], sPtr->size, sPtr->b, sPtr->c, sPtr->d, sPtr->e, sPtr->f); } else { spline5(&xPtr[ofst], &yPtr[ofst], sPtr->size-1, sPtr->b, sPtr->c, sPtr->d, sPtr->e, sPtr->f); } } } /*` Build Quadratic Sectors */ for(i=0; i < profilePtr->numOfSectors; i++) { if(piPtr->secPtr.curve_fit_type == QUAD_SPLINE) { /*` If one QUAD_SPLINE found, build all quads */ spline2build(profilePtr, permutation); break; } } /*` IF build not aborted */ if(BUILD_ABORTED != piPtr->state) { /*` Build Cubic Sectors */ for(i=0; i < profilePtr->numOfSectors; i++) { if(piPtr->secPtr.curve_fit_type == CUBIC_SPLINE) { /*` If one CUBIC_SPLINE found, build all cubics */ spline3build(profilePtr, permutation); break; } } } /*` ENDIF */ } /*` ENDIF */ /*` Building all done */ /*` IF aborted build */ if(BUILD_ABORTED == piPtr->state) { /*` Set the status */ status = CAM_PROFILE_BUILD_FAILED; /* Already been logged where state was set */ } /*` Else */ else { /*` Set state to BUILT */ piPtr->state = BUILT; } /*` ENDIF */ } /*` ELSE */ else{ /*` Log Error, b/c profile instance is null */ status = CAM_UNEXPECTED_PATH; ERR_log_new_event(EVENTID_INTERNAL_SW_STATUS_ERROR, MAKE_EVENTID_INFO(EVENT_MODID,ERR_ERROR,MODULE_INDEX), MAKE_EVENTID_DATA_LINENUM_SCODE(status)); } /*` ENDIF */ } return status; } |
---|---|
楼主最近还看过
工控网智造工程师好文精选