Bläddra i källkod

PLSR 多段路径执行器落地,并完成 P0 接口纠偏

【P1 多段路径执行器】新增 plsr_path.c/.h,在任务快照与十态状态机之上实现段级执行语义:
- 六种等待条件(完成/WAIT时间/WAIT信号/ACT/EXT/EXT或完成),ACT 可截断未完成段,段先完则等 ACT
- EXT 按 0→1 新边沿触发,段开始已有效的电平不误触发
- 跳转支持顺序、指定段、自循环,动态跳转(D/HD/FD)每次跳转时重新求值,运行期越界转 ERROR
- 绝对模式零位移与相对零脉冲段直接跳转;每轮最多 100 次连续无脉冲跳转后让出 CPU
- 状态转换表放宽 RUN/DECEL→ACCEL(段间衔接为正常路径);新增 SEGMENT_COMPLETE 事件与 1ms tick 推进
- STATUS 增加 currentSegment 诊断字段;STOP/PAUSE/急停/限位/故障时路径立即终止

【P0 接口纠偏】
- HSD 改为 16 位 WORD 语义(两个相邻 WORD 组成 32 位值),新增 Dword 读写 API,对外回绕并置溢出诊断
- HSD 检查点记录升级 v2:持久化 position_valid/last_busy,运动中掉电恢复后位置不可信
- SET/CLEAR_POSITION、启动、完成/停止/错误均触发 HSD 检查点(此前生产流程从未保存)
- SFD/K0 出厂默认值补齐(FOLLOW=50、最大速度 100000 等),全新 Flash 可直接启动
- directionDelayUs 改名 directionDelayMs;PlsrPostStart 隐藏为仅测试可用
- 新增 test_plsr_path(13 场景 227 项);四套 host 测试共 696 项全绿,IAR 0 错误 0 警告
deepseek
ywh 1 månad sedan
förälder
incheckning
2fe534c39f
18 ändrade filer med 5190 tillägg och 89 borttagningar
  1. +6
    -0
      EWARM/Modbus.ewp
  2. +12
    -7
      PLSR/Inc/plc_device.h
  3. +6
    -1
      PLSR/Inc/plsr_core.h
  4. +1
    -1
      PLSR/Inc/plsr_job.h
  5. +71
    -0
      PLSR/Inc/plsr_path.h
  6. +9
    -3
      PLSR/Inc/plsr_persistence.h
  7. +4
    -1
      PLSR/Inc/plsr_types.h
  8. +59
    -17
      PLSR/Src/plc_device.c
  9. +183
    -16
      PLSR/Src/plsr_core.c
  10. +5
    -8
      PLSR/Src/plsr_job.c
  11. +456
    -0
      PLSR/Src/plsr_path.c
  12. +80
    -10
      PLSR/Src/plsr_persistence.c
  13. +14
    -0
      PLSR/Test/run_host_tests.ps1
  14. +93
    -25
      PLSR/Test/test_plc_device.c
  15. +48
    -0
      PLSR/Test/test_plsr_core.c
  16. +48
    -0
      PLSR/Test/test_plsr_job.c
  17. +682
    -0
      PLSR/Test/test_plsr_path.c
  18. +3413
    -0
      tmp/manual_p11_97.txt

+ 6
- 0
EWARM/Modbus.ewp Visa fil

@@ -1284,6 +1284,9 @@
<file>
<name>$PROJ_DIR$\..\PLSR\Inc\plsr_job.h</name>
</file>
<file>
<name>$PROJ_DIR$\..\PLSR\Inc\plsr_path.h</name>
</file>
<file>
<name>$PROJ_DIR$\..\PLSR\Src\plsr_persistence.c</name>
</file>
@@ -1296,6 +1299,9 @@
<file>
<name>$PROJ_DIR$\..\PLSR\Src\plsr_job.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\PLSR\Src\plsr_path.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\PLSR\Src\plsr_core.c</name>
</file>


+ 12
- 7
PLSR/Inc/plc_device.h Visa fil

@@ -23,15 +23,20 @@ typedef enum

PLC_DEVICE_RESULT PlcDeviceInit(void);

PLC_DEVICE_RESULT PlcDeviceReadHsd(uint16_t address, int32_t *value);
PLC_DEVICE_RESULT PlcDeviceWriteHsdConfig(uint16_t address, int32_t value);
PLC_DEVICE_RESULT PlcDevicePublishHsdRuntime(uint16_t address, int32_t value);
PLC_DEVICE_RESULT PlcDeviceReadHsdPair(uint16_t lowAddress, int64_t *value);
PLC_DEVICE_RESULT PlcDevicePublishHsdPair(uint16_t lowAddress, int64_t value);
PLC_DEVICE_RESULT PlcDeviceReadHsd(uint16_t address, uint16_t *value);
PLC_DEVICE_RESULT PlcDeviceWriteHsdConfig(uint16_t address, uint16_t value);
PLC_DEVICE_RESULT PlcDevicePublishHsdRuntime(uint16_t address, uint16_t value);
PLC_DEVICE_RESULT PlcDeviceReadHsdDword(uint16_t lowAddress, int32_t *value);
PLC_DEVICE_RESULT PlcDevicePublishHsdDword(uint16_t lowAddress, int32_t value);
PLC_DEVICE_RESULT PlcDeviceCheckpointHsd(void);

PLC_DEVICE_RESULT PlcDeviceReadSfd(uint16_t address, int32_t *value);
PLC_DEVICE_RESULT PlcDeviceWriteSfd(uint16_t address, int32_t value);
PLC_DEVICE_RESULT PlcDeviceSetHsdCheckpointMeta(uint8_t positionValid,
uint8_t lastBusy);
uint8_t PlcDeviceGetRestoredHsdPositionValid(void);
uint8_t PlcDeviceGetRestoredHsdLastBusy(void);

PLC_DEVICE_RESULT PlcDeviceReadSfd(uint16_t address, uint16_t *value);
PLC_DEVICE_RESULT PlcDeviceWriteSfd(uint16_t address, uint16_t value);
PLC_DEVICE_RESULT PlcDeviceLoadSfd(void);
PLC_DEVICE_RESULT PlcDeviceSaveSfd(void);
PLC_DEVICE_RESULT PlcDeviceResetSfdDefaults(void);


+ 6
- 1
PLSR/Inc/plsr_core.h Visa fil

@@ -13,7 +13,6 @@ PLSR_RESULT PlsrInit(void);
void PlsrTask(void *argument);
void PlsrProcess(void);

PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request);
PLSR_RESULT PlsrPostCall(const PLSR_CALL *call);
PLSR_RESULT PlsrPostCommand(const PLSR_COMMAND *command);
PLSR_RESULT PlsrPostEvent(uint8_t axis, uint32_t eventMask);
@@ -21,6 +20,12 @@ PLSR_RESULT PlsrGetStatus(uint8_t axis, PLSR_STATUS *status);
PLSR_RESULT PlsrGetLastParseDetail(uint8_t axis,
PLSR_PARSE_DETAIL *detail);

#ifdef PLSR_HOST_TEST
/* 仅测试使用的低层启动入口:不解析 S0/S1/S2/D,直接申请资源进入 ACCEL。
* 生产调用必须使用 PlsrPostCall()。 */
PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request);
#endif

PLSR_RESULT PlsrStateTransition(uint8_t axis,
PLSR_STATE target,
PLSR_TRANSITION_REASON reason);


+ 1
- 1
PLSR/Inc/plsr_job.h Visa fil

@@ -124,7 +124,7 @@ typedef struct
uint16_t accelerationMs;
uint16_t decelerationMs;
uint16_t gapAccelerationMs;
uint16_t directionDelayUs;
uint16_t directionDelayMs;
uint8_t curveMode;
uint8_t follow;
uint8_t feedforwardPercent;


+ 71
- 0
PLSR/Inc/plsr_path.h Visa fil

@@ -0,0 +1,71 @@
#ifndef PLSR_PATH_H
#define PLSR_PATH_H

#include "plsr_job.h"
#include "plsr_types.h"
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

#define PLSR_PATH_ZERO_JUMP_BUDGET (100U)

typedef enum
{
PLSR_PATH_SEGMENT_RUNNING = 0,
PLSR_PATH_WAIT_TIME,
PLSR_PATH_WAIT_SIGNAL,
PLSR_PATH_ACT_PENDING,
PLSR_PATH_ENDED
} PLSR_PATH_PHASE;

typedef enum
{
PLSR_PATH_ACTION_NONE = 0,
PLSR_PATH_ACTION_NEXT_SEGMENT,
PLSR_PATH_ACTION_ENTER_WAIT,
PLSR_PATH_ACTION_JOB_COMPLETE,
PLSR_PATH_ACTION_YIELD,
PLSR_PATH_ACTION_ERROR
} PLSR_PATH_ACTION;

typedef struct
{
PLSR_PATH_PHASE phase;
uint16_t currentSegment;
uint16_t nextSegment;
uint8_t waitCondition;
uint32_t waitRemainingMs;
uint16_t zeroJumpBudget;
uint8_t lastExtLevel;
uint8_t segmentEnded;
uint8_t extActive;
uint8_t actActive;
uint8_t jumpChainPending;
uint8_t jobEnded;
uint8_t waitSignalDevice;
uint32_t waitSignalAddress;
} PLSR_PATH_CONTEXT;

void PlsrPathBegin(PLSR_PATH_CONTEXT *context,
const PLSR_JOB_SNAPSHOT *job,
int64_t logicalPosition);

PLSR_PATH_ACTION PlsrPathOnSegmentDone(PLSR_PATH_CONTEXT *context,
const PLSR_JOB_SNAPSHOT *job,
int64_t logicalPosition);

PLSR_PATH_ACTION PlsrPathTick(PLSR_PATH_CONTEXT *context,
const PLSR_JOB_SNAPSHOT *job,
int64_t logicalPosition);

uint16_t PlsrPathGetCurrentSegment(const PLSR_PATH_CONTEXT *context);

void PlsrPathTerminate(PLSR_PATH_CONTEXT *context);

#ifdef __cplusplus
}
#endif

#endif /* PLSR_PATH_H */

+ 9
- 3
PLSR/Inc/plsr_persistence.h Visa fil

@@ -8,15 +8,21 @@
extern "C" {
#endif

/* HSD 检查点元数据位(随 HSD 记录一起掉电保存)。 */
#define PLSR_HSD_META_POSITION_VALID (1UL << 0U)
#define PLSR_HSD_META_LAST_BUSY (1UL << 1U)
#define PLSR_HSD_META_REASON_MASK (0x0000003CUL)

typedef struct
{
int32_t runtime[PLSR_HSD_RUNTIME_COUNT];
int32_t config[PLSR_HSD_CONFIG_COUNT];
uint32_t metadata;
uint16_t runtime[PLSR_HSD_RUNTIME_COUNT];
uint16_t config[PLSR_HSD_CONFIG_COUNT];
} PLSR_HSD_DATA;

typedef struct
{
int32_t config[PLSR_SFD_CONFIG_COUNT];
uint16_t config[PLSR_SFD_CONFIG_COUNT];
} PLSR_SFD_DATA;

typedef enum


+ 4
- 1
PLSR/Inc/plsr_types.h Visa fil

@@ -129,8 +129,9 @@ typedef enum
#define PLSR_EVENT_WAIT_BEGIN (1UL << 10U)
#define PLSR_EVENT_WAIT_COMPLETE (1UL << 11U)
#define PLSR_EVENT_JOB_COMPLETE (1UL << 12U)
#define PLSR_EVENT_SEGMENT_COMPLETE (1UL << 13U)

#define PLSR_EVENT_ALL_MASK (0x00001F3FUL)
#define PLSR_EVENT_ALL_MASK (0x00003F3FUL)
#define PLSR_EVENT_CRITICAL_MASK (PLSR_EVENT_SOFTWARE_EMERGENCY \
| PLSR_EVENT_LIMIT_POSITIVE \
| PLSR_EVENT_LIMIT_NEGATIVE \
@@ -177,8 +178,10 @@ typedef struct
uint8_t jobValid;
uint8_t s2Set;
uint8_t speedClamped;
uint8_t positionOverflow;
uint16_t segmentCount;
uint16_t startSegment;
uint16_t currentSegment;
} PLSR_STATUS;

#ifdef __cplusplus


+ 59
- 17
PLSR/Src/plc_device.c Visa fil

@@ -26,6 +26,8 @@ static uint8_t PlcSfdOperationActive;
static uint32_t PlcHsdChangeCounter;
static PLSR_PERSISTENCE_RESULT PlcLastHsdLoadResult;
static PLSR_PERSISTENCE_RESULT PlcLastSfdLoadResult;
static uint8_t PlcRestoredHsdPositionValid;
static uint8_t PlcRestoredHsdLastBusy;

static uint32_t PlcDeviceEnterCritical(void)
{
@@ -90,7 +92,7 @@ static void PlcDeviceEndSfdOperation(void)
PlcDeviceExitCritical(interruptState);
}

static int32_t *PlcDeviceResolveHsd(uint16_t address)
static uint16_t *PlcDeviceResolveHsd(uint16_t address)
{
if (address < PLSR_HSD_RUNTIME_START + PLSR_HSD_RUNTIME_COUNT)
{
@@ -171,6 +173,8 @@ PLC_DEVICE_RESULT PlcDeviceInit(void)
(void)memset(PlcSmFlags, 0, sizeof(PlcSmFlags));
PlcSfdOperationActive = 0U;
PlcHsdChangeCounter = 0UL;
PlcRestoredHsdPositionValid = 0U;
PlcRestoredHsdLastBusy = 0U;

PlcLastHsdLoadResult = PlsrPersistenceLoadHsd(&PlcHsdData);
PlcHsdDirty = (PlcLastHsdLoadResult == PLSR_PERSISTENCE_DEFAULTED)
@@ -181,6 +185,12 @@ PLC_DEVICE_RESULT PlcDeviceInit(void)
{
return PLC_DEVICE_PERSISTENCE_ERROR;
}
PlcRestoredHsdPositionValid =
((PlcHsdData.metadata & PLSR_HSD_META_POSITION_VALID) != 0UL)
? 1U
: 0U;
PlcRestoredHsdLastBusy =
((PlcHsdData.metadata & PLSR_HSD_META_LAST_BUSY) != 0UL) ? 1U : 0U;

PlcLastSfdLoadResult = PlsrPersistenceLoadSfd(&PlcSfdData);
PlcSfdDirty = (PlcLastSfdLoadResult == PLSR_PERSISTENCE_OK) ? 0U : 1U;
@@ -193,9 +203,9 @@ PLC_DEVICE_RESULT PlcDeviceInit(void)
return PLC_DEVICE_OK;
}

PLC_DEVICE_RESULT PlcDeviceReadHsd(uint16_t address, int32_t *value)
PLC_DEVICE_RESULT PlcDeviceReadHsd(uint16_t address, uint16_t *value)
{
int32_t *source;
uint16_t *source;

if (value == NULL)
{
@@ -211,7 +221,7 @@ PLC_DEVICE_RESULT PlcDeviceReadHsd(uint16_t address, int32_t *value)
return PLC_DEVICE_OK;
}

PLC_DEVICE_RESULT PlcDeviceWriteHsdConfig(uint16_t address, int32_t value)
PLC_DEVICE_RESULT PlcDeviceWriteHsdConfig(uint16_t address, uint16_t value)
{
if ((address < PLSR_HSD_CONFIG_START)
|| (address >= PLSR_HSD_CONFIG_START + PLSR_HSD_CONFIG_COUNT))
@@ -228,7 +238,7 @@ PLC_DEVICE_RESULT PlcDeviceWriteHsdConfig(uint16_t address, int32_t value)
return PLC_DEVICE_OK;
}

PLC_DEVICE_RESULT PlcDevicePublishHsdRuntime(uint16_t address, int32_t value)
PLC_DEVICE_RESULT PlcDevicePublishHsdRuntime(uint16_t address, uint16_t value)
{
if (address >= PLSR_HSD_RUNTIME_START + PLSR_HSD_RUNTIME_COUNT)
{
@@ -241,9 +251,10 @@ PLC_DEVICE_RESULT PlcDevicePublishHsdRuntime(uint16_t address, int32_t value)
return PLC_DEVICE_OK;
}

PLC_DEVICE_RESULT PlcDeviceReadHsdPair(uint16_t lowAddress, int64_t *value)
/* 两个相邻 WORD 组成一个 32 位值:低地址=低16位,高地址=高16位(信捷兼容布局)。 */
PLC_DEVICE_RESULT PlcDeviceReadHsdDword(uint16_t lowAddress, int32_t *value)
{
uint64_t combined;
uint32_t combined;
uint32_t interruptState;
uint16_t offset;

@@ -260,16 +271,16 @@ PLC_DEVICE_RESULT PlcDeviceReadHsdPair(uint16_t lowAddress, int64_t *value)

offset = (uint16_t)(lowAddress - PLSR_HSD_RUNTIME_START);
interruptState = PlcDeviceEnterCritical();
combined = (uint64_t)(uint32_t)PlcHsdData.runtime[offset];
combined |= ((uint64_t)(uint32_t)PlcHsdData.runtime[offset + 1U]) << 32U;
combined = (uint32_t)PlcHsdData.runtime[offset];
combined |= ((uint32_t)PlcHsdData.runtime[offset + 1U]) << 16U;
PlcDeviceExitCritical(interruptState);
*value = (int64_t)combined;
*value = (int32_t)combined;
return PLC_DEVICE_OK;
}

PLC_DEVICE_RESULT PlcDevicePublishHsdPair(uint16_t lowAddress, int64_t value)
PLC_DEVICE_RESULT PlcDevicePublishHsdDword(uint16_t lowAddress, int32_t value)
{
uint64_t rawValue;
uint32_t rawValue;
uint32_t interruptState;
uint16_t offset;

@@ -280,17 +291,48 @@ PLC_DEVICE_RESULT PlcDevicePublishHsdPair(uint16_t lowAddress, int64_t value)
return PLC_DEVICE_INVALID_ADDRESS;
}

rawValue = (uint64_t)value;
rawValue = (uint32_t)value;
offset = (uint16_t)(lowAddress - PLSR_HSD_RUNTIME_START);
interruptState = PlcDeviceEnterCritical();
PlcHsdData.runtime[offset] = (int32_t)(uint32_t)rawValue;
PlcHsdData.runtime[offset + 1U] = (int32_t)(uint32_t)(rawValue >> 32U);
PlcHsdData.runtime[offset] = (uint16_t)(rawValue & 0xFFFFUL);
PlcHsdData.runtime[offset + 1U] = (uint16_t)(rawValue >> 16U);
PlcHsdChangeCounter++;
PlcHsdDirty = 1U;
PlcDeviceExitCritical(interruptState);
return PLC_DEVICE_OK;
}

PLC_DEVICE_RESULT PlcDeviceSetHsdCheckpointMeta(uint8_t positionValid,
uint8_t lastBusy)
{
uint32_t interruptState = PlcDeviceEnterCritical();

PlcHsdData.metadata &= ~(PLSR_HSD_META_POSITION_VALID
| PLSR_HSD_META_LAST_BUSY);
if (positionValid != 0U)
{
PlcHsdData.metadata |= PLSR_HSD_META_POSITION_VALID;
}
if (lastBusy != 0U)
{
PlcHsdData.metadata |= PLSR_HSD_META_LAST_BUSY;
}
PlcHsdChangeCounter++;
PlcHsdDirty = 1U;
PlcDeviceExitCritical(interruptState);
return PLC_DEVICE_OK;
}

uint8_t PlcDeviceGetRestoredHsdPositionValid(void)
{
return PlcRestoredHsdPositionValid;
}

uint8_t PlcDeviceGetRestoredHsdLastBusy(void)
{
return PlcRestoredHsdLastBusy;
}

PLC_DEVICE_RESULT PlcDeviceCheckpointHsd(void)
{
PLSR_HSD_DATA snapshot;
@@ -323,7 +365,7 @@ PLC_DEVICE_RESULT PlcDeviceCheckpointHsd(void)
return PLC_DEVICE_OK;
}

PLC_DEVICE_RESULT PlcDeviceReadSfd(uint16_t address, int32_t *value)
PLC_DEVICE_RESULT PlcDeviceReadSfd(uint16_t address, uint16_t *value)
{
uint32_t interruptState;

@@ -348,7 +390,7 @@ PLC_DEVICE_RESULT PlcDeviceReadSfd(uint16_t address, int32_t *value)
return PLC_DEVICE_OK;
}

PLC_DEVICE_RESULT PlcDeviceWriteSfd(uint16_t address, int32_t value)
PLC_DEVICE_RESULT PlcDeviceWriteSfd(uint16_t address, uint16_t value)
{
uint32_t interruptState;



+ 183
- 16
PLSR/Src/plsr_core.c Visa fil

@@ -1,6 +1,7 @@
#include "plsr_core.h"
#include "plc_device.h"
#include "plsr_address_map.h"
#include "plsr_path.h"
#include "plsr_resource.h"
#include <string.h>

@@ -25,12 +26,14 @@ typedef struct
uint32_t illegalTransitionCount;
int64_t logicalPosition;
int64_t totalPulses;
PLSR_PATH_CONTEXT path;
uint8_t hasLastCommand;
uint8_t done;
uint8_t directionPositive;
uint8_t immediateStopPending;
uint8_t positionValid;
uint8_t jobValid;
uint8_t positionOverflow;
} PLSR_AXIS;

typedef struct
@@ -113,6 +116,23 @@ static void PlsrSetStopReason(PLSR_AXIS *axis,
}
}

/* 将 64 位逻辑位置发布为 HSD 的 32 位兼容值:
* 超出 INT32 范围时回绕(与信捷 32 位寄存器一致)并置溢出诊断标志。 */
static void PlsrPublishPosition(uint8_t axis, PLSR_AXIS *axisObject)
{
int32_t compatValue;

if ((axisObject->logicalPosition > INT32_MAX)
|| (axisObject->logicalPosition < INT32_MIN))
{
axisObject->positionOverflow = 1U;
}
compatValue = (int32_t)(uint32_t)axisObject->logicalPosition;
(void)PlcDevicePublishHsdDword(
(uint16_t)((uint16_t)axis * PLSR_HSD_RUNTIME_AXIS_COUNT),
compatValue);
}

static uint8_t PlsrTransitionIsAllowed(PLSR_STATE current,
PLSR_STATE target)
{
@@ -141,7 +161,8 @@ static uint8_t PlsrTransitionIsAllowed(PLSR_STATE current,

case PLSR_STATE_ACCEL:
case PLSR_STATE_RUN:
return ((target == PLSR_STATE_RUN)
return ((target == PLSR_STATE_ACCEL)
|| (target == PLSR_STATE_RUN)
|| (target == PLSR_STATE_DECEL)
|| (target == PLSR_STATE_WAIT)
|| (target == PLSR_STATE_COMPLETED)
@@ -150,7 +171,8 @@ static uint8_t PlsrTransitionIsAllowed(PLSR_STATE current,
: 0U;

case PLSR_STATE_DECEL:
return ((target == PLSR_STATE_RUN)
return ((target == PLSR_STATE_ACCEL)
|| (target == PLSR_STATE_RUN)
|| (target == PLSR_STATE_WAIT)
|| (target == PLSR_STATE_PAUSED)
|| (target == PLSR_STATE_COMPLETED)
@@ -223,7 +245,11 @@ PLSR_RESULT PlsrStateTransition(uint8_t axis,
{
axisObject->pendingTerminal = PLSR_STATE_UNINITIALIZED;
axisObject->immediateStopPending = 0U;
PlsrPathTerminate(&axisObject->path);
PlsrResourceRelease(&axisObject->lease);
/* 运动已结束:记录 lastBusy=0,保证掉电后恢复时位置仍可信。 */
(void)PlcDeviceSetHsdCheckpointMeta(axisObject->positionValid, 0U);
(void)PlcDeviceCheckpointHsd();
}
PlsrPublishAxis(axis);
return PLSR_RESULT_OK;
@@ -360,6 +386,7 @@ static uint8_t PlsrPopHighestPriorityCommand(PLSR_COMMAND_SLOT *slot)
return 1U;
}

#ifdef PLSR_HOST_TEST
PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request)
{
PLSR_COMMAND command;
@@ -383,6 +410,7 @@ PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request)
command.argument = 0;
return PlsrQueueCommand(&command, request, NULL);
}
#endif /* PLSR_HOST_TEST */

PLSR_RESULT PlsrPostCall(const PLSR_CALL *call)
{
@@ -515,6 +543,12 @@ static PLSR_RESULT PlsrStartAxis(PLSR_AXIS *axisObject,
{
PlsrResourceRelease(&axisObject->lease);
}
else
{
/* 运动开始:掉电恢复时据此判定"断电时在运动中"。 */
(void)PlcDeviceSetHsdCheckpointMeta(axisObject->positionValid, 1U);
(void)PlcDeviceCheckpointHsd();
}
return result;
}

@@ -568,6 +602,19 @@ static PLSR_RESULT PlsrStartCall(PLSR_AXIS *axisObject,
axisObject->pendingTerminal = PLSR_STATE_UNINITIALIZED;
axisObject->immediateStopPending = 0U;
axisObject->done = 0U;
PlsrPathBegin(&axisObject->path,
&axisObject->job,
axisObject->logicalPosition);
if (axisObject->path.jobEnded != 0U)
{
/* 起点就是零脉冲且跳转链已结束(任务无实际脉冲)。 */
axisObject->stopReason = PLSR_STOP_REASON_NORMAL_COMPLETE;
(void)PlsrStateTransition(call->dAxis,
PLSR_STATE_COMPLETED,
PLSR_TRANSITION_JOB_COMPLETE);
axisObject->jobValid = 0U;
return PLSR_RESULT_OK;
}
result = PlsrStateTransition(call->dAxis,
PLSR_STATE_ACCEL,
PLSR_TRANSITION_START);
@@ -576,6 +623,12 @@ static PLSR_RESULT PlsrStartCall(PLSR_AXIS *axisObject,
axisObject->jobValid = 0U;
PlsrResourceRelease(&axisObject->lease);
}
else
{
/* 运动开始:掉电恢复时据此判定"断电时在运动中"。 */
(void)PlcDeviceSetHsdCheckpointMeta(axisObject->positionValid, 1U);
(void)PlcDeviceCheckpointHsd();
}
return result;
}

@@ -600,6 +653,7 @@ static PLSR_RESULT PlsrStopImmediate(uint8_t axis)

PlsrSetStopReason(axisObject, PLSR_STOP_REASON_STOP_IMMEDIATE);
axisObject->pendingTerminal = PLSR_STATE_STOPPED;
PlsrPathTerminate(&axisObject->path);
if ((axisObject->state == PLSR_STATE_WAIT)
|| (axisObject->state == PLSR_STATE_PAUSED))
{
@@ -633,6 +687,7 @@ static PLSR_RESULT PlsrStopDecel(uint8_t axis)

PlsrSetStopReason(axisObject, PLSR_STOP_REASON_STOP_DECEL);
axisObject->pendingTerminal = PLSR_STATE_STOPPED;
PlsrPathTerminate(&axisObject->path);
if ((axisObject->state == PLSR_STATE_WAIT)
|| (axisObject->state == PLSR_STATE_PAUSED))
{
@@ -664,6 +719,7 @@ static PLSR_RESULT PlsrPause(uint8_t axis)
if (axisObject->state == PLSR_STATE_WAIT)
{
PlsrSetStopReason(axisObject, PLSR_STOP_REASON_PAUSE);
PlsrPathTerminate(&axisObject->path);
return PlsrStateTransition(axis,
PLSR_STATE_PAUSED,
PLSR_TRANSITION_DECEL_COMPLETE);
@@ -677,6 +733,7 @@ static PLSR_RESULT PlsrPause(uint8_t axis)

PlsrSetStopReason(axisObject, PLSR_STOP_REASON_PAUSE);
axisObject->pendingTerminal = PLSR_STATE_PAUSED;
PlsrPathTerminate(&axisObject->path);
if (axisObject->state == PLSR_STATE_DECEL)
{
return PLSR_RESULT_OK;
@@ -743,10 +800,10 @@ static PLSR_RESULT PlsrExecuteCommand(const PLSR_COMMAND_SLOT *slot)
{
axisObject->logicalPosition = slot->command.argument;
axisObject->positionValid = 1U;
(void)PlcDevicePublishHsdPair(
(uint16_t)(slot->command.axis
* PLSR_HSD_RUNTIME_AXIS_COUNT),
axisObject->logicalPosition);
axisObject->positionOverflow = 0U;
PlsrPublishPosition(slot->command.axis, axisObject);
(void)PlcDeviceSetHsdCheckpointMeta(1U, 0U);
(void)PlcDeviceCheckpointHsd();
result = PLSR_RESULT_OK;
}
break;
@@ -760,10 +817,10 @@ static PLSR_RESULT PlsrExecuteCommand(const PLSR_COMMAND_SLOT *slot)
{
axisObject->logicalPosition = 0;
axisObject->positionValid = 1U;
(void)PlcDevicePublishHsdPair(
(uint16_t)(slot->command.axis
* PLSR_HSD_RUNTIME_AXIS_COUNT),
0);
axisObject->positionOverflow = 0U;
PlsrPublishPosition(slot->command.axis, axisObject);
(void)PlcDeviceSetHsdCheckpointMeta(1U, 0U);
(void)PlcDeviceCheckpointHsd();
result = PLSR_RESULT_OK;
}
break;
@@ -854,6 +911,7 @@ static void PlsrProcessCriticalEvents(uint8_t axis, uint32_t events)
PlsrSetStopReason(axisObject,
PLSR_STOP_REASON_SOFTWARE_EMERGENCY);
axisObject->done = 0U;
PlsrPathTerminate(&axisObject->path);
(void)PlsrStateTransition(axis,
PLSR_STATE_STOPPED,
PLSR_TRANSITION_STOP);
@@ -870,6 +928,7 @@ static void PlsrProcessCriticalEvents(uint8_t axis, uint32_t events)
PlsrSetStopReason(axisObject,
PLSR_STOP_REASON_LIMIT_POSITIVE);
axisObject->pendingTerminal = PLSR_STATE_STOPPED;
PlsrPathTerminate(&axisObject->path);
if ((axisObject->state == PLSR_STATE_WAIT)
|| (axisObject->state == PLSR_STATE_PAUSED))
{
@@ -896,6 +955,7 @@ static void PlsrProcessCriticalEvents(uint8_t axis, uint32_t events)
PlsrSetStopReason(axisObject,
PLSR_STOP_REASON_LIMIT_NEGATIVE);
axisObject->pendingTerminal = PLSR_STATE_STOPPED;
PlsrPathTerminate(&axisObject->path);
if ((axisObject->state == PLSR_STATE_WAIT)
|| (axisObject->state == PLSR_STATE_PAUSED))
{
@@ -920,12 +980,75 @@ static void PlsrProcessCriticalEvents(uint8_t axis, uint32_t events)
: PLSR_ERROR_COUNTER_FAULT;
PlsrSetStopReason(axisObject, PLSR_STOP_REASON_FAULT);
axisObject->done = 0U;
PlsrPathTerminate(&axisObject->path);
(void)PlsrStateTransition(axis,
PLSR_STATE_ERROR,
PLSR_TRANSITION_FAULT);
}
}

/* 应用路径执行器的动作:段间推进、进入等待、结束、让出、错误。 */
static void PlsrApplyPathAction(uint8_t axis, PLSR_PATH_ACTION action)
{
PLSR_AXIS *axisObject = &PlsrAxes[axis];

switch (action)
{
case PLSR_PATH_ACTION_NEXT_SEGMENT:
if (axisObject->state == PLSR_STATE_WAIT)
{
(void)PlsrStateTransition(axis,
PLSR_STATE_ACCEL,
PLSR_TRANSITION_WAIT_COMPLETE);
}
else if ((axisObject->state == PLSR_STATE_ACCEL)
|| (axisObject->state == PLSR_STATE_RUN)
|| (axisObject->state == PLSR_STATE_DECEL))
{
(void)PlsrStateTransition(axis,
PLSR_STATE_ACCEL,
PLSR_TRANSITION_START);
}
break;

case PLSR_PATH_ACTION_ENTER_WAIT:
if ((axisObject->state == PLSR_STATE_ACCEL)
|| (axisObject->state == PLSR_STATE_RUN))
{
(void)PlsrStateTransition(axis,
PLSR_STATE_WAIT,
PLSR_TRANSITION_WAIT_BEGIN);
}
break;

case PLSR_PATH_ACTION_JOB_COMPLETE:
if (PlsrStateIsBusy(axisObject->state) != 0U)
{
axisObject->stopReason = PLSR_STOP_REASON_NORMAL_COMPLETE;
(void)PlsrStateTransition(axis,
PLSR_STATE_COMPLETED,
PLSR_TRANSITION_JOB_COMPLETE);
}
break;

case PLSR_PATH_ACTION_YIELD:
/* 预算耗尽:本轮不再推进,下个 tick 由 PlsrPathTick 恢复。 */
break;

case PLSR_PATH_ACTION_ERROR:
axisObject->error = PLSR_ERROR_INTERNAL;
PlsrSetStopReason(axisObject, PLSR_STOP_REASON_FAULT);
axisObject->done = 0U;
(void)PlsrStateTransition(axis,
PLSR_STATE_ERROR,
PLSR_TRANSITION_FAULT);
break;

default:
break;
}
}

static void PlsrProcessNormalEvents(uint8_t axis, uint32_t events)
{
PLSR_AXIS *axisObject = &PlsrAxes[axis];
@@ -988,6 +1111,19 @@ static void PlsrProcessNormalEvents(uint8_t axis, uint32_t events)
PLSR_TRANSITION_WAIT_COMPLETE);
}

if (((events & PLSR_EVENT_SEGMENT_COMPLETE) != 0UL)
&& (axisObject->state == PLSR_STATE_RUN)
&& (axisObject->pendingTerminal == PLSR_STATE_UNINITIALIZED)
&& (axisObject->immediateStopPending == 0U))
{
PLSR_PATH_ACTION action = PlsrPathOnSegmentDone(
&axisObject->path,
&axisObject->job,
axisObject->logicalPosition);

PlsrApplyPathAction(axis, action);
}

if (((events & PLSR_EVENT_JOB_COMPLETE) != 0UL)
&& (PlsrStateIsBusy(axisObject->state) != 0U)
&& (axisObject->pendingTerminal == PLSR_STATE_UNINITIALIZED)
@@ -1002,7 +1138,9 @@ static void PlsrProcessNormalEvents(uint8_t axis, uint32_t events)

PLSR_RESULT PlsrInit(void)
{
int64_t restoredPosition;
int32_t restoredPosition;
uint8_t restoredPositionValid;
uint8_t restoredLastBusy;
uint8_t axis;

(void)memset(PlsrAxes, 0, sizeof(PlsrAxes));
@@ -1011,18 +1149,23 @@ PLSR_RESULT PlsrInit(void)
PlsrResourceInit();
PlsrInitialized = 1U;

restoredPositionValid = PlcDeviceGetRestoredHsdPositionValid();
restoredLastBusy = PlcDeviceGetRestoredHsdLastBusy();
for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++)
{
PlsrAxes[axis].state = PLSR_STATE_UNINITIALIZED;
PlsrAxes[axis].pendingTerminal = PLSR_STATE_UNINITIALIZED;
PlsrAxes[axis].lease.directionPoint = PLSR_DIRECTION_POINT_NONE;
if ((PlcDeviceGetLastHsdLoadResult() == PLSR_PERSISTENCE_OK)
&& (PlcDeviceReadHsdPair(
(uint16_t)(axis * PLSR_HSD_RUNTIME_AXIS_COUNT),
&restoredPosition)
== PLC_DEVICE_OK))
if (PlcDeviceReadHsdDword(
(uint16_t)(axis * PLSR_HSD_RUNTIME_AXIS_COUNT),
&restoredPosition) == PLC_DEVICE_OK)
{
PlsrAxes[axis].logicalPosition = restoredPosition;
}
/* 只有上次正常停机且保存了位置有效标志,才允许绝对定位;
* 运动中掉电(lastBusy=1)时位置不可信。 */
if ((restoredPositionValid != 0U) && (restoredLastBusy == 0U))
{
PlsrAxes[axis].positionValid = 1U;
}
if (PlsrStateTransition(axis,
@@ -1074,6 +1217,25 @@ void PlsrProcess(void)
PlsrProcessNormalEvents(axis, events);
}
}

/* 1ms tick:路径执行器推进(WAIT/ACT 计时、信号/EXT 轮询、跳转链)。 */
for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++)
{
PLSR_AXIS *axisObject = &PlsrAxes[axis];
PLSR_PATH_ACTION action;

if (PlsrStateIsBusy(axisObject->state) == 0U)
{
continue;
}
action = PlsrPathTick(&axisObject->path,
&axisObject->job,
axisObject->logicalPosition);
if (action != PLSR_PATH_ACTION_NONE)
{
PlsrApplyPathAction(axis, action);
}
}
}

void PlsrTask(void *argument)
@@ -1127,6 +1289,7 @@ PLSR_RESULT PlsrGetStatus(uint8_t axis, PLSR_STATUS *status)
: PLSR_DIRECTION_POINT_NONE;
status->positionValid = axisObject->positionValid;
status->jobValid = axisObject->jobValid;
status->positionOverflow = axisObject->positionOverflow;
status->s2Set = (axisObject->jobValid != 0U) ? axisObject->job.s2Set : 0U;
status->speedClamped = (axisObject->jobValid != 0U)
? axisObject->job.speedClamped
@@ -1137,6 +1300,10 @@ PLSR_RESULT PlsrGetStatus(uint8_t axis, PLSR_STATUS *status)
status->startSegment = (axisObject->jobValid != 0U)
? axisObject->job.startSegment
: 0U;
status->currentSegment = (axisObject->jobValid != 0U)
? PlsrPathGetCurrentSegment(
&axisObject->path)
: 0U;
PlsrCoreExitCritical(interruptState);
return PLSR_RESULT_OK;
}


+ 5
- 8
PLSR/Src/plsr_job.c Visa fil

@@ -188,23 +188,20 @@ static PLSR_RESULT PlsrReadFixedWord(uint8_t useHsd,
uint16_t *value,
PLSR_PARSE_DETAIL *detail)
{
int32_t rawValue = 0;
PLC_DEVICE_RESULT deviceResult;

deviceResult = (useHsd != 0U) ? PlcDeviceReadHsd(address, &rawValue)
: PlcDeviceReadSfd(address, &rawValue);
if ((deviceResult != PLC_DEVICE_OK) || (rawValue < 0)
|| (rawValue > (int32_t)UINT16_MAX))
deviceResult = (useHsd != 0U) ? PlcDeviceReadHsd(address, value)
: PlcDeviceReadSfd(address, value);
if (deviceResult != PLC_DEVICE_OK)
{
PlsrSetDetail(detail,
PLSR_RESULT_INVALID_S2,
PLSR_PARSE_BLOCK_S2,
address,
rawValue,
0,
0U);
return PLSR_RESULT_INVALID_S2;
}
*value = (uint16_t)rawValue;
return PLSR_RESULT_OK;
}

@@ -523,7 +520,7 @@ static PLSR_RESULT PlsrLoadS2(const PLSR_CALL *call,
snapshot->directionPoint = (uint8_t)word;
result = PlsrReadFixedWord(0U,
(uint16_t)(commonBase + 7U),
&snapshot->s2.directionDelayUs,
&snapshot->s2.directionDelayMs,
detail);
if (result != PLSR_RESULT_OK) return result;



+ 456
- 0
PLSR/Src/plsr_path.c Visa fil

@@ -0,0 +1,456 @@
#include "plsr_path.h"
#include <string.h>

/* 等待寄存器类型编码(PLSR_VALUE_SOURCE)与软元件类型(PLSR_DEVICE_TYPE)
* 相差 1:D=1→D、HD=2→HD、FD=3→FD、X=4→X、M=5→M、HM=6→HM。 */
static uint8_t PlsrPathDeviceFromSource(uint8_t sourceCode)
{
return (uint8_t)(sourceCode - 1U);
}

static uint8_t PlsrPathReadDword(const PLSR_DATA_SOURCE *source,
PLSR_DEVICE_TYPE device,
uint32_t address,
int32_t *value)
{
uint16_t lowWord;
uint16_t highWord;

if ((source->readWord == NULL)
|| (source->readWord(source->context,
device,
address,
&lowWord) == 0U)
|| (source->readWord(source->context,
device,
address + 1U,
&highWord) == 0U))
{
return 0U;
}
*value = (int32_t)(((uint32_t)highWord << 16U) | (uint32_t)lowWord);
return 1U;
}

static uint8_t PlsrPathReadBit(const PLSR_DATA_SOURCE *source,
PLSR_DEVICE_TYPE device,
uint32_t address,
uint8_t *value)
{
if ((source->readBit == NULL)
|| (source->readBit(source->context, device, address, value) == 0U))
{
return 0U;
}
return 1U;
}

/* 求值当前段的跳转目标。
* 返回:0=顺序下一段,1~segmentCount=指定段。
* 动态来源在跳转发生时重新读取;非法值置 error。 */
static int32_t PlsrPathResolveJumpTarget(const PLSR_JOB_SNAPSHOT *job,
uint16_t segment,
uint8_t *error)
{
const PLSR_SEGMENT_SNAPSHOT *segmentSnapshot =
&job->segments[segment - 1U];
int32_t target;

*error = 0U;
if (segmentSnapshot->jumpSource == PLSR_VALUE_CONSTANT)
{
target = segmentSnapshot->jumpValueOrAddress;
}
else
{
if (PlsrPathReadDword(&job->source,
(PLSR_DEVICE_TYPE)PlsrPathDeviceFromSource(
segmentSnapshot->jumpSource),
(uint32_t)segmentSnapshot->jumpValueOrAddress,
&target) == 0U)
{
*error = 1U;
return 0;
}
}
if ((target < 0) || (target > (int32_t)job->segmentCount))
{
*error = 1U;
return 0;
}
return target;
}

/* 段是否零脉冲:相对模式脉冲数为 0,或绝对模式目标等于进入段时的位置。 */
static uint8_t PlsrPathSegmentIsZeroPulse(const PLSR_JOB_SNAPSHOT *job,
uint16_t segment,
int64_t logicalPosition)
{
const PLSR_SEGMENT_SNAPSHOT *segmentSnapshot =
&job->segments[segment - 1U];

if ((segmentSnapshot->flags & PLSR_SEGMENT_FLAG_ZERO_PULSE) != 0U)
{
return 1U;
}
if ((job->positioningMode == 1U)
&& (segmentSnapshot->pulseOrTarget == (int32_t)logicalPosition))
{
return 1U;
}
return 0U;
}

/* 进入新段时的初始化:求值 ACT 时间、EXT/等待信号地址与当前电平。 */
static void PlsrPathEnterSegment(PLSR_PATH_CONTEXT *context,
const PLSR_JOB_SNAPSHOT *job)
{
const PLSR_SEGMENT_SNAPSHOT *segmentSnapshot =
&job->segments[context->currentSegment - 1U];
int32_t actValue;

context->segmentEnded = 0U;
context->extActive = 0U;
context->actActive = 0U;
context->waitCondition = (uint8_t)segmentSnapshot->waitCondition;

switch (context->waitCondition)
{
case PLSR_WAIT_ACT_TIME:
context->actActive = 1U;
if (segmentSnapshot->waitSource == PLSR_VALUE_CONSTANT)
{
actValue = segmentSnapshot->waitValueOrAddress;
}
else
{
actValue = 0;
(void)PlsrPathReadDword(&job->source,
(PLSR_DEVICE_TYPE)
PlsrPathDeviceFromSource(
segmentSnapshot->waitSource),
(uint32_t)
segmentSnapshot->waitValueOrAddress,
&actValue);
}
context->waitRemainingMs =
(actValue > 0) ? (uint32_t)actValue : 0U;
break;

case PLSR_WAIT_SIGNAL:
case PLSR_WAIT_EXT:
case PLSR_WAIT_EXT_OR_COMPLETE:
context->waitSignalDevice =
PlsrPathDeviceFromSource(segmentSnapshot->waitSource);
context->waitSignalAddress =
(uint32_t)segmentSnapshot->waitValueOrAddress;
if (context->waitCondition != PLSR_WAIT_SIGNAL)
{
/* EXT:记录段开始时的电平,已有的有效电平不算新边沿。 */
uint8_t level = 0U;

(void)PlsrPathReadBit(&job->source,
(PLSR_DEVICE_TYPE)
context->waitSignalDevice,
context->waitSignalAddress,
&level);
context->lastExtLevel = level;
context->extActive = 1U;
}
break;

default:
break;
}
}

/* EXT 边沿检测:电平 0→1 才算新边沿。 */
static uint8_t PlsrPathPollExt(PLSR_PATH_CONTEXT *context,
const PLSR_JOB_SNAPSHOT *job)
{
uint8_t level = 0U;
uint8_t triggered = 0U;

if (PlsrPathReadBit(&job->source,
(PLSR_DEVICE_TYPE)context->waitSignalDevice,
context->waitSignalAddress,
&level) != 0U)
{
if ((level != 0U) && (context->lastExtLevel == 0U))
{
triggered = 1U;
}
context->lastExtLevel = level;
}
return triggered;
}

/* 推进跳转链:零脉冲段连续跳转(预算内),直到有脉冲段、结束或预算耗尽。 */
static PLSR_PATH_ACTION PlsrPathAdvance(PLSR_PATH_CONTEXT *context,
const PLSR_JOB_SNAPSHOT *job,
int64_t logicalPosition)
{
uint8_t error;
int32_t target;
uint16_t next;

while (context->zeroJumpBudget > 0U)
{
target = PlsrPathResolveJumpTarget(job,
context->currentSegment,
&error);
if (error != 0U)
{
context->jobEnded = 1U;
context->phase = PLSR_PATH_ENDED;
return PLSR_PATH_ACTION_ERROR;
}
if (target == 0)
{
if (context->currentSegment >= job->segmentCount)
{
context->jobEnded = 1U;
context->phase = PLSR_PATH_ENDED;
return PLSR_PATH_ACTION_JOB_COMPLETE;
}
next = (uint16_t)(context->currentSegment + 1U);
}
else
{
next = (uint16_t)target;
}

if (PlsrPathSegmentIsZeroPulse(job, next, logicalPosition) != 0U)
{
context->zeroJumpBudget--;
context->currentSegment = next;
context->nextSegment = next;
PlsrPathEnterSegment(context, job);
continue;
}

context->currentSegment = next;
context->nextSegment = next;
context->phase = PLSR_PATH_SEGMENT_RUNNING;
PlsrPathEnterSegment(context, job);
return PLSR_PATH_ACTION_NEXT_SEGMENT;
}

context->jumpChainPending = 1U;
return PLSR_PATH_ACTION_YIELD;
}

void PlsrPathBegin(PLSR_PATH_CONTEXT *context,
const PLSR_JOB_SNAPSHOT *job,
int64_t logicalPosition)
{
(void)memset(context, 0, sizeof(*context));
context->currentSegment = job->startSegment;
context->nextSegment = context->currentSegment;
context->zeroJumpBudget = PLSR_PATH_ZERO_JUMP_BUDGET;
context->phase = PLSR_PATH_SEGMENT_RUNNING;
PlsrPathEnterSegment(context, job);
if (PlsrPathSegmentIsZeroPulse(job,
context->currentSegment,
logicalPosition) != 0U)
{
/* 起点段就是零脉冲:直接走跳转链。 */
(void)PlsrPathAdvance(context, job, logicalPosition);
}
}

PLSR_PATH_ACTION PlsrPathOnSegmentDone(PLSR_PATH_CONTEXT *context,
const PLSR_JOB_SNAPSHOT *job,
int64_t logicalPosition)
{
const PLSR_SEGMENT_SNAPSHOT *segmentSnapshot;
int32_t waitValue;
uint8_t level;

if ((context->jobEnded != 0U) || (context->phase == PLSR_PATH_ENDED))
{
return PLSR_PATH_ACTION_NONE;
}
context->segmentEnded = 1U;
context->zeroJumpBudget = PLSR_PATH_ZERO_JUMP_BUDGET;
context->jumpChainPending = 0U;
segmentSnapshot = &job->segments[context->currentSegment - 1U];

switch (context->waitCondition)
{
case PLSR_WAIT_PULSE_COMPLETE:
case PLSR_WAIT_EXT_OR_COMPLETE:
return PlsrPathAdvance(context, job, logicalPosition);

case PLSR_WAIT_TIME:
/* 等待时间在段完成时求值一次。 */
if (segmentSnapshot->waitSource == PLSR_VALUE_CONSTANT)
{
waitValue = segmentSnapshot->waitValueOrAddress;
}
else
{
waitValue = 0;
(void)PlsrPathReadDword(
&job->source,
(PLSR_DEVICE_TYPE)PlsrPathDeviceFromSource(
segmentSnapshot->waitSource),
(uint32_t)segmentSnapshot->waitValueOrAddress,
&waitValue);
}
context->waitRemainingMs =
(waitValue > 0) ? (uint32_t)waitValue : 0U;
if (context->waitRemainingMs == 0U)
{
return PlsrPathAdvance(context, job, logicalPosition);
}
context->phase = PLSR_PATH_WAIT_TIME;
return PLSR_PATH_ACTION_ENTER_WAIT;

case PLSR_WAIT_SIGNAL:
/* 信号地址已在进入段时求值;先立即检查一次,已有效则直接跳。 */
if (PlsrPathReadBit(&job->source,
(PLSR_DEVICE_TYPE)context->waitSignalDevice,
context->waitSignalAddress,
&level) != 0U)
{
if (level != 0U)
{
return PlsrPathAdvance(context, job, logicalPosition);
}
}
context->phase = PLSR_PATH_WAIT_SIGNAL;
return PLSR_PATH_ACTION_ENTER_WAIT;

case PLSR_WAIT_ACT_TIME:
if (context->waitRemainingMs == 0U)
{
return PlsrPathAdvance(context, job, logicalPosition);
}
/* 段已完成但 ACT 未到:停在 ACT_PENDING(core 处于 WAIT)。 */
context->phase = PLSR_PATH_ACT_PENDING;
return PLSR_PATH_ACTION_ENTER_WAIT;

case PLSR_WAIT_EXT:
/* 段已完成但 EXT 未触发:继续等新边沿。 */
context->phase = PLSR_PATH_WAIT_SIGNAL;
return PLSR_PATH_ACTION_ENTER_WAIT;

default:
return PlsrPathAdvance(context, job, logicalPosition);
}
}

PLSR_PATH_ACTION PlsrPathTick(PLSR_PATH_CONTEXT *context,
const PLSR_JOB_SNAPSHOT *job,
int64_t logicalPosition)
{
uint8_t level;

if ((context->jobEnded != 0U) || (context->phase == PLSR_PATH_ENDED))
{
return PLSR_PATH_ACTION_NONE;
}

if (context->jumpChainPending != 0U)
{
/* 上轮预算耗尽让出:本轮重置预算继续跳转链。 */
context->zeroJumpBudget = PLSR_PATH_ZERO_JUMP_BUDGET;
context->jumpChainPending = 0U;
return PlsrPathAdvance(context, job, logicalPosition);
}

switch (context->phase)
{
case PLSR_PATH_SEGMENT_RUNNING:
if ((context->actActive != 0U)
&& (context->waitRemainingMs > 0U))
{
context->waitRemainingMs--;
if (context->waitRemainingMs == 0U)
{
/* ACT 到时:截断当前段,立即跳转。 */
return PlsrPathAdvance(context, job, logicalPosition);
}
}
if ((context->extActive != 0U)
&& (PlsrPathPollExt(context, job) != 0U))
{
return PlsrPathAdvance(context, job, logicalPosition);
}
break;

case PLSR_PATH_WAIT_TIME:
if (context->waitRemainingMs > 0U)
{
context->waitRemainingMs--;
}
if (context->waitRemainingMs == 0U)
{
return PlsrPathAdvance(context, job, logicalPosition);
}
break;

case PLSR_PATH_WAIT_SIGNAL:
if (context->waitCondition == PLSR_WAIT_SIGNAL)
{
/* H02:电平有效即触发。 */
if (PlsrPathReadBit(&job->source,
(PLSR_DEVICE_TYPE)
context->waitSignalDevice,
context->waitSignalAddress,
&level) != 0U)
{
if (level != 0U)
{
return PlsrPathAdvance(context, job, logicalPosition);
}
}
}
else
{
/* H04 段完成后的 EXT 等待:新边沿触发。 */
if (PlsrPathPollExt(context, job) != 0U)
{
return PlsrPathAdvance(context, job, logicalPosition);
}
}
break;

case PLSR_PATH_ACT_PENDING:
if (context->waitRemainingMs > 0U)
{
context->waitRemainingMs--;
}
if (context->waitRemainingMs == 0U)
{
return PlsrPathAdvance(context, job, logicalPosition);
}
break;

default:
break;
}
return PLSR_PATH_ACTION_NONE;
}

uint16_t PlsrPathGetCurrentSegment(const PLSR_PATH_CONTEXT *context)
{
if (context == NULL)
{
return 0U;
}
return context->currentSegment;
}

void PlsrPathTerminate(PLSR_PATH_CONTEXT *context)
{
if (context == NULL)
{
return;
}
context->phase = PLSR_PATH_ENDED;
context->jobEnded = 1U;
context->jumpChainPending = 0U;
context->zeroJumpBudget = 0U;
}

+ 80
- 10
PLSR/Src/plsr_persistence.c Visa fil

@@ -3,7 +3,7 @@
#include <string.h>

#define PLSR_HSD_BACKUP_MAGIC (0x504C4853UL)
#define PLSR_HSD_BACKUP_VERSION (1U)
#define PLSR_HSD_BACKUP_VERSION (2U)
#define PLSR_BACKUP_SLOT_A_OFFSET (0x0100UL)
#define PLSR_BACKUP_SLOT_STRIDE (0x0200UL)
#define PLSR_SFD_FLASH_MAGIC (0x504C5346UL)
@@ -16,6 +16,12 @@
#define PLSR_SFD_PARAMETER_SET_OFFSET (50U)
#define PLSR_SFD_PARAMETER_SET_STRIDE (20U)
#define PLSR_SFD_DEFAULT_MAX_SPEED (100000UL)
#define PLSR_SFD_DEFAULT_SPEED (1000UL)
#define PLSR_SFD_DEFAULT_ACCEL_MS (100U)
#define PLSR_SFD_DEFAULT_DECEL_MS (100U)
#define PLSR_SFD_DEFAULT_FOLLOW (50U)
#define PLSR_HSD_PARAMETER_SET_STRIDE (20U)
#define PLSR_HSD_DEFAULT_BASE (460U)

typedef struct
{
@@ -167,6 +173,32 @@ static void PlsrPersistenceCopySfdFromVolatile(
}
}

static void PlsrPersistenceApplyParameterSetDefaults(uint16_t *config,
uint16_t base)
{
/* 与信捷出厂默认一致:默认速度1000、加减速100ms、最大速度100000、
* 起始/终止速度0、FOLLOW 50、前馈0、1ms刷新。 */
config[base + 0U] = (uint16_t)(PLSR_SFD_DEFAULT_SPEED & 0xFFFFUL);
config[base + 1U] = (uint16_t)(PLSR_SFD_DEFAULT_SPEED >> 16U);
config[base + 2U] = (uint16_t)PLSR_SFD_DEFAULT_ACCEL_MS;
config[base + 3U] = (uint16_t)PLSR_SFD_DEFAULT_DECEL_MS;
config[base + 4U] = 0U;
config[base + 5U] = 0U;
config[base + 6U] = (uint16_t)(PLSR_SFD_DEFAULT_MAX_SPEED & 0xFFFFUL);
config[base + 7U] = (uint16_t)(PLSR_SFD_DEFAULT_MAX_SPEED >> 16U);
config[base + 8U] = 0U;
config[base + 9U] = 0U;
config[base + 10U] = 0U;
config[base + 11U] = 0U;
config[base + 12U] = (uint16_t)PLSR_SFD_DEFAULT_FOLLOW;
config[base + 13U] = 0U;
config[base + 14U] = 0U;
config[base + 16U] = 0U;
config[base + 17U] = 0U;
config[base + 18U] = 0U;
config[base + 19U] = 0U;
}

static void PlsrPersistenceApplySfdDefaults(PLSR_SFD_DATA *data)
{
uint16_t axisOffset;
@@ -179,10 +211,35 @@ static void PlsrPersistenceApplySfdDefaults(PLSR_SFD_DATA *data)
{
axisOffset = (uint16_t)((uint16_t)axis * PLSR_SFD_AXIS_STRIDE);

/* Explicit Xinje defaults from the SFD parameter table. */
data->config[axisOffset + 7U] = 10;
data->config[axisOffset + 27U] = 20;
data->config[axisOffset + 43U] = 0x0201;
/* 公共参数(与信捷出厂默认一致)。 */
data->config[axisOffset + 0U] = 0; /* SFD900 方向逻辑/单位等 */
data->config[axisOffset + 1U] = 0; /* SFD901 完成模式 */
data->config[axisOffset + 2U] = 1; /* SFD902 脉冲数/1转=1 */
data->config[axisOffset + 3U] = 0;
data->config[axisOffset + 4U] = 1; /* SFD904 移动量/1转=1 */
data->config[axisOffset + 5U] = 0;
data->config[axisOffset + 6U] = 0xFF; /* SFD906 方向端子:无 */
data->config[axisOffset + 7U] = 10; /* SFD907 方向延时10ms */
data->config[axisOffset + 8U] = 0; /* SFD908 正向齿隙 */
data->config[axisOffset + 9U] = 0; /* SFD909 负向齿隙 */
data->config[axisOffset + 12U] = 0; /* SFD912 端子开关状态 */
data->config[axisOffset + 13U] = 0xFF; /* SFD913 原点端子:无 */
data->config[axisOffset + 14U] = 0xFF; /* SFD914 Z相端子:无 */
data->config[axisOffset + 15U] = 0xFFFF; /* SFD915 极限端子:无 */
data->config[axisOffset + 17U] = 0xFF; /* SFD917 CLR端子:无 */
data->config[axisOffset + 18U] = 0; /* SFD918 VH */
data->config[axisOffset + 19U] = 0;
data->config[axisOffset + 22U] = 0; /* SFD922 VC */
data->config[axisOffset + 23U] = 0;
data->config[axisOffset + 24U] = 0; /* SFD924 机械原点位置 */
data->config[axisOffset + 25U] = 0;
data->config[axisOffset + 26U] = 0; /* SFD926 Z相个数 */
data->config[axisOffset + 27U] = 20; /* SFD927 CLR延时20ms */
data->config[axisOffset + 30U] = 0; /* SFD930 软限位正 */
data->config[axisOffset + 31U] = 0;
data->config[axisOffset + 32U] = 0; /* SFD932 软限位负 */
data->config[axisOffset + 33U] = 0;
data->config[axisOffset + 43U] = 0x0201; /* SFD943 默认参数块 */

for (parameterSet = 0U;
parameterSet < PLSR_SFD_PARAMETER_SET_COUNT;
@@ -191,14 +248,26 @@ static void PlsrPersistenceApplySfdDefaults(PLSR_SFD_DATA *data)
parameterOffset = (uint16_t)(
axisOffset + PLSR_SFD_PARAMETER_SET_OFFSET
+ (uint16_t)parameterSet * PLSR_SFD_PARAMETER_SET_STRIDE);
data->config[parameterOffset + 6U] =
(int32_t)(PLSR_SFD_DEFAULT_MAX_SPEED & 0xFFFFUL);
data->config[parameterOffset + 7U] =
(int32_t)(PLSR_SFD_DEFAULT_MAX_SPEED >> 16U);
PlsrPersistenceApplyParameterSetDefaults(data->config,
parameterOffset);
}
}
}

static void PlsrPersistenceApplyHsdDefaults(PLSR_HSD_DATA *data)
{
uint8_t axis;

(void)memset(data, 0, sizeof(*data));
for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++)
{
/* config 数组索引 = HSD地址 - PLSR_HSD_CONFIG_START。 */
PlsrPersistenceApplyParameterSetDefaults(
data->config,
(uint16_t)((uint16_t)axis * PLSR_HSD_PARAMETER_SET_STRIDE));
}
}

static PLSR_PERSISTENCE_RESULT PlsrPersistenceBeginSfdOperation(void)
{
#ifdef PLSR_HOST_TEST
@@ -324,6 +393,7 @@ static void PlsrPersistenceCopyHsdFromVolatile(
{
uint16_t index;

destination->metadata = source->metadata;
for (index = 0U; index < PLSR_HSD_RUNTIME_COUNT; index++)
{
destination->runtime[index] = source->runtime[index];
@@ -385,7 +455,7 @@ PLSR_PERSISTENCE_RESULT PlsrPersistenceLoadHsd(PLSR_HSD_DATA *data)

if ((validA == 0U) && (validB == 0U))
{
(void)memset(data, 0, sizeof(*data));
PlsrPersistenceApplyHsdDefaults(data);
return PLSR_PERSISTENCE_DEFAULTED;
}



+ 14
- 0
PLSR/Test/run_host_tests.ps1 Visa fil

@@ -29,6 +29,7 @@ $tests = @(
"$workspacePath\PLSR\Src\plsr_persistence.c"
"$workspacePath\PLSR\Src\plsr_resource.c"
"$workspacePath\PLSR\Src\plsr_job.c"
"$workspacePath\PLSR\Src\plsr_path.c"
"$workspacePath\PLSR\Src\plsr_core.c"
"$workspacePath\PLSR\Test\test_plsr_core.c"
)
@@ -40,9 +41,22 @@ $tests = @(
"$workspacePath\PLSR\Src\plsr_persistence.c"
"$workspacePath\PLSR\Src\plsr_resource.c"
"$workspacePath\PLSR\Src\plsr_job.c"
"$workspacePath\PLSR\Src\plsr_path.c"
"$workspacePath\PLSR\Src\plsr_core.c"
"$workspacePath\PLSR\Test\test_plsr_job.c"
)
},
@{
Name = 'test_plsr_path'
Sources = @(
"$workspacePath\PLSR\Src\plc_device.c"
"$workspacePath\PLSR\Src\plsr_persistence.c"
"$workspacePath\PLSR\Src\plsr_resource.c"
"$workspacePath\PLSR\Src\plsr_job.c"
"$workspacePath\PLSR\Src\plsr_path.c"
"$workspacePath\PLSR\Src\plsr_core.c"
"$workspacePath\PLSR\Test\test_plsr_path.c"
)
}
)



+ 93
- 25
PLSR/Test/test_plc_device.c Visa fil

@@ -40,10 +40,10 @@ static void TestAddressMap(void)

static void TestHsdAndPersistence(void)
{
uint16_t value16;
int32_t value32;
int64_t value64;
const int64_t firstPair = INT64_C(0x1234567887654321);
const int64_t secondPair = -INT64_C(0x102030405060708);
const int32_t firstDword = INT32_C(0x12345678);
const int32_t secondDword = -INT32_C(0x1020304);

PlsrPersistenceTestResetStorage();
TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK);
@@ -51,38 +51,95 @@ static void TestHsdAndPersistence(void)
TEST_CHECK(PlcDeviceIsHsdDirty() != 0U);

TEST_CHECK(PlcDeviceWriteHsdConfig(460U, 11) == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceWriteHsdConfig(539U, -22) == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceWriteHsdConfig(539U, 0xB4E2U) == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceWriteHsdConfig(0U, 1) == PLC_DEVICE_READ_ONLY);
TEST_CHECK(PlcDeviceWriteHsdConfig(459U, 1) == PLC_DEVICE_INVALID_ADDRESS);
TEST_CHECK(PlcDeviceReadHsd(460U, &value32) == PLC_DEVICE_OK);
TEST_CHECK(value32 == 11);
TEST_CHECK(PlcDeviceReadHsd(539U, &value32) == PLC_DEVICE_OK);
TEST_CHECK(value32 == -22);
TEST_CHECK(PlcDeviceReadHsd(540U, &value32) == PLC_DEVICE_INVALID_ADDRESS);

TEST_CHECK(PlcDevicePublishHsdPair(0U, firstPair) == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceReadHsdPair(0U, &value64) == PLC_DEVICE_OK);
TEST_CHECK(value64 == firstPair);
TEST_CHECK(PlcDeviceReadHsdPair(1U, &value64) == PLC_DEVICE_INVALID_ADDRESS);
TEST_CHECK(PlcDeviceReadHsd(460U, &value16) == PLC_DEVICE_OK);
TEST_CHECK(value16 == 11);
TEST_CHECK(PlcDeviceReadHsd(539U, &value16) == PLC_DEVICE_OK);
TEST_CHECK(value16 == 0xB4E2U);
TEST_CHECK(PlcDeviceReadHsd(540U, &value16) == PLC_DEVICE_INVALID_ADDRESS);

/* 32 位兼容布局:低地址=低16位,高地址=高16位。 */
TEST_CHECK(PlcDevicePublishHsdDword(0U, firstDword) == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceReadHsd(0U, &value16) == PLC_DEVICE_OK);
TEST_CHECK(value16 == 0x5678U);
TEST_CHECK(PlcDeviceReadHsd(1U, &value16) == PLC_DEVICE_OK);
TEST_CHECK(value16 == 0x1234U);
TEST_CHECK(PlcDeviceReadHsdDword(0U, &value32) == PLC_DEVICE_OK);
TEST_CHECK(value32 == firstDword);
TEST_CHECK(PlcDeviceReadHsdDword(1U, &value32)
== PLC_DEVICE_INVALID_ADDRESS);
TEST_CHECK(PlcDeviceReadHsdDword(15U, &value32)
== PLC_DEVICE_INVALID_ADDRESS);
TEST_CHECK(PlcDeviceCheckpointHsd() == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceIsHsdDirty() == 0U);

TEST_CHECK(PlcDeviceWriteHsdConfig(460U, 22) == PLC_DEVICE_OK);
TEST_CHECK(PlcDevicePublishHsdPair(0U, secondPair) == PLC_DEVICE_OK);
TEST_CHECK(PlcDevicePublishHsdDword(0U, secondDword) == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceCheckpointHsd() == PLC_DEVICE_OK);

PlsrPersistenceTestCorruptNewestHsd();
TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceGetLastHsdLoadResult() == PLSR_PERSISTENCE_OK);
TEST_CHECK(PlcDeviceReadHsd(460U, &value32) == PLC_DEVICE_OK);
TEST_CHECK(value32 == 11);
TEST_CHECK(PlcDeviceReadHsdPair(0U, &value64) == PLC_DEVICE_OK);
TEST_CHECK(value64 == firstPair);
TEST_CHECK(PlcDeviceReadHsd(460U, &value16) == PLC_DEVICE_OK);
TEST_CHECK(value16 == 11);
TEST_CHECK(PlcDeviceReadHsdDword(0U, &value32) == PLC_DEVICE_OK);
TEST_CHECK(value32 == firstDword);
}

static void TestHsdDefaults(void)
{
uint16_t value;

PlsrPersistenceTestResetStorage();
TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceGetLastHsdLoadResult() == PLSR_PERSISTENCE_DEFAULTED);

/* K0(HSD460~539)出厂默认即可直接启动。 */
TEST_CHECK(PlcDeviceReadHsd(460U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == 1000); /* 默认速度低16位 */
TEST_CHECK(PlcDeviceReadHsd(461U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == 0); /* 默认速度高16位 */
TEST_CHECK(PlcDeviceReadHsd(462U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == 100); /* 加速时间 ms */
TEST_CHECK(PlcDeviceReadHsd(463U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == 100); /* 减速时间 ms */
TEST_CHECK(PlcDeviceReadHsd(466U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == 0x86A0U); /* 最大速度低16位 100000 */
TEST_CHECK(PlcDeviceReadHsd(467U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == 1); /* 最大速度高16位 */
TEST_CHECK(PlcDeviceReadHsd(472U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == 50); /* FOLLOW 1~100 */
TEST_CHECK(PlcDeviceReadHsd(539U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == 0);
}

static void TestHsdMetaPersistence(void)
{
PlsrPersistenceTestResetStorage();
TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceGetRestoredHsdPositionValid() == 0U);
TEST_CHECK(PlcDeviceGetRestoredHsdLastBusy() == 0U);

/* 正常停机保存:positionValid=1, lastBusy=0。 */
TEST_CHECK(PlcDeviceSetHsdCheckpointMeta(1U, 0U) == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceCheckpointHsd() == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceGetRestoredHsdPositionValid() == 1U);
TEST_CHECK(PlcDeviceGetRestoredHsdLastBusy() == 0U);

/* 运动中掉电保存:lastBusy=1,恢复后位置不可信。 */
TEST_CHECK(PlcDeviceSetHsdCheckpointMeta(1U, 1U) == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceCheckpointHsd() == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceGetRestoredHsdPositionValid() == 1U);
TEST_CHECK(PlcDeviceGetRestoredHsdLastBusy() == 1U);
}

static void TestSfd(void)
{
int32_t value;
uint16_t value;

PlsrPersistenceTestResetStorage();
TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK);
@@ -99,6 +156,15 @@ static void TestSfd(void)
TEST_CHECK(value == 0x86A0);
TEST_CHECK(PlcDeviceReadSfd(957U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == 1);
/* 第1套参数出厂默认可直接启动。 */
TEST_CHECK(PlcDeviceReadSfd(950U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == 1000);
TEST_CHECK(PlcDeviceReadSfd(951U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == 0);
TEST_CHECK(PlcDeviceReadSfd(952U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == 100);
TEST_CHECK(PlcDeviceReadSfd(962U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == 50);
TEST_CHECK(PlcDeviceReadSfd(1297U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == 10);
TEST_CHECK(PlsrPersistenceLoadSfd(NULL)
@@ -107,11 +173,11 @@ static void TestSfd(void)
== PLSR_PERSISTENCE_INVALID_ARGUMENT);

TEST_CHECK(PlcDeviceWriteSfd(900U, 123) == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceWriteSfd(1419U, -456) == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceWriteSfd(1419U, 0xB456U) == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceReadSfd(900U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == 123);
TEST_CHECK(PlcDeviceReadSfd(1419U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == -456);
TEST_CHECK(value == 0xB456U);
TEST_CHECK(PlcDeviceReadSfd(899U, &value) == PLC_DEVICE_INVALID_ADDRESS);
TEST_CHECK(PlcDeviceReadSfd(1420U, &value) == PLC_DEVICE_INVALID_ADDRESS);
TEST_CHECK(PlcDeviceIsSfdDirty() != 0U);
@@ -123,17 +189,17 @@ static void TestSfd(void)
TEST_CHECK(PlcDeviceReadSfd(900U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == 123);
TEST_CHECK(PlcDeviceReadSfd(1419U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == -456);
TEST_CHECK(value == 0xB456U);

TEST_CHECK(PlcDeviceWriteSfd(900U, 789) == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceWriteSfd(1419U, -987) == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceWriteSfd(1419U, 0x9876U) == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceSaveSfd() == PLC_DEVICE_OK);
PlsrPersistenceTestCorruptNewestSfd();
TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceReadSfd(900U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == 123);
TEST_CHECK(PlcDeviceReadSfd(1419U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == -456);
TEST_CHECK(value == 0xB456U);

TEST_CHECK(PlcDeviceWriteSfd(900U, 1001) == PLC_DEVICE_OK);
PlsrPersistenceTestSetSfdFault(PLSR_TEST_SFD_FAULT_ERASE);
@@ -215,6 +281,8 @@ int main(void)
{
TestAddressMap();
TestHsdAndPersistence();
TestHsdDefaults();
TestHsdMetaPersistence();
TestSfd();
TestSmAndSd();



+ 48
- 0
PLSR/Test/test_plsr_core.c Visa fil

@@ -344,12 +344,60 @@ static void TestQueueAndInputValidation(void)
TEST_CHECK(TestGetStatus(0U).lastCommandResult == PLSR_RESULT_OK);
}

static void TestPositionCheckpointing(void)
{
PLSR_STATUS status;
int32_t hsdPosition;

/* 1. SET_POSITION 后立即写入 HSD 检查点。 */
TestReset();
TEST_CHECK(TestPostCommand(1U, 0U, PLSR_CMD_SET_POSITION, 500)
== PLSR_RESULT_QUEUED);
PlsrProcess();
status = TestGetStatus(0U);
TEST_CHECK(status.lastCommandResult == PLSR_RESULT_OK);
TEST_CHECK(status.positionValid != 0U);
TEST_CHECK(PlcDeviceIsHsdDirty() == 0U);
TEST_CHECK(PlcDeviceReadHsdDword(0U, &hsdPosition) == PLC_DEVICE_OK);
TEST_CHECK(hsdPosition == 500);

/* 2. 模拟重启:位置与 positionValid 应恢复到上次正常停机的检查点。 */
TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK);
TEST_CHECK(PlsrInit() == PLSR_RESULT_OK);
status = TestGetStatus(0U);
TEST_CHECK(status.positionValid != 0U);
TEST_CHECK(status.logicalPosition == 500);

/* 3. 运动中掉电(lastBusy=1):恢复后位置不可信。 */
TEST_CHECK(PlcDeviceSetHsdCheckpointMeta(1U, 1U) == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceCheckpointHsd() == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK);
TEST_CHECK(PlsrInit() == PLSR_RESULT_OK);
status = TestGetStatus(0U);
TEST_CHECK(status.positionValid == 0U);
TEST_CHECK(status.logicalPosition == 500);

/* 4. 64位位置超出 INT32 范围:对外回绕并置溢出诊断。 */
TEST_CHECK(TestPostCommand(2U,
0U,
PLSR_CMD_SET_POSITION,
INT64_C(0x100000000))
== PLSR_RESULT_QUEUED);
PlsrProcess();
status = TestGetStatus(0U);
TEST_CHECK(status.lastCommandResult == PLSR_RESULT_OK);
TEST_CHECK(status.positionOverflow != 0U);
TEST_CHECK(PlcDeviceReadHsdDword(0U, &hsdPosition) == PLC_DEVICE_OK);
TEST_CHECK(hsdPosition == 0);
}

int main(void)
{
TestResourceManager();
TestStateMachineAndCommands();
TestLimitWaitAndPairs();
TestQueueAndInputValidation();
TestPositionCheckpointing();

(void)printf("PASS: %u PLSR core checks\n", TestCount);
return EXIT_SUCCESS;


+ 48
- 0
PLSR/Test/test_plsr_job.c Visa fil

@@ -387,6 +387,53 @@ static void TestCoreSubmission(void)
CHECK(detail.result == PLSR_RESULT_OK);
}

static void TestDefaultSfdStartable(void)
{
TEST_MEMORY memory;
PLSR_CALL call;
PLSR_STATUS status;

/* 全新 Flash:默认 S2 参数(FOLLOW=50、最大速度=100000 等)应可直接启动。 */
PlsrPersistenceTestResetStorage();
CHECK(PlcDeviceInit() == PLC_DEVICE_OK);
CHECK(PlcDeviceGetLastSfdLoadResult() == PLSR_PERSISTENCE_DEFAULTED);

TestBuildValidBlocks(&memory);
call = TestMakeCall(&memory);
call.sequence = 600UL;

/* 方向端子是接线参数,未配置(0xFF)时 PULSE/DIR 应报资源错误。 */
CHECK(PlsrInit() == PLSR_RESULT_OK);
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
PlsrProcess();
CHECK(PlsrGetStatus(0U, &status) == PLSR_RESULT_OK);
CHECK(status.lastCommandResult == PLSR_RESULT_INVALID_RESOURCE);
CHECK(status.state == PLSR_STATE_IDLE);

/* 用户配置方向端子后,其余出厂默认参数应能直接启动。 */
CHECK(PlcDeviceWriteSfd(906U, 4) == PLC_DEVICE_OK);
call.sequence = 601UL;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
PlsrProcess();
CHECK(PlsrGetStatus(0U, &status) == PLSR_RESULT_OK);
CHECK(status.lastCommandResult == PLSR_RESULT_OK);
CHECK(status.state == PLSR_STATE_ACCEL);
CHECK(status.jobValid == 1U);
CHECK(status.s2Set == 1U);
CHECK(status.speedClamped == 1U);
CHECK(status.directionPoint == 4U);

/* 回到 IDLE,避免资源泄漏影响后续。 */
CHECK(PlsrPostCommand(&(PLSR_COMMAND){602U, 0U, PLSR_CMD_STOP_IMMEDIATE, 0})
== PLSR_RESULT_QUEUED);
PlsrProcess();
CHECK(PlsrPostEvent(0U, PLSR_EVENT_STOP_IMMEDIATE_DONE)
== PLSR_RESULT_OK);
PlsrProcess();
CHECK(PlsrGetStatus(0U, &status) == PLSR_RESULT_OK);
CHECK(status.state == PLSR_STATE_STOPPED);
}

int main(void)
{
PlsrPersistenceTestResetStorage();
@@ -396,6 +443,7 @@ int main(void)
TestValidationFailures();
TestDynamicReferences();
TestOutputAndDivider();
TestDefaultSfdStartable();
TestCoreSubmission();

if (TestFailures != 0)


+ 682
- 0
PLSR/Test/test_plsr_path.c Visa fil

@@ -0,0 +1,682 @@
#include "plc_device.h"
#include "plsr_core.h"
#include "plsr_job.h"
#include "plsr_path.h"
#include "plsr_persistence.h"
#include <stdio.h>
#include <string.h>

#define TEST_WORD_CAPACITY (3000U)
#define TEST_BIT_CAPACITY (128U)

#define TEST_S0_BASE (100U)
#define TEST_S1_BASE (200U)
#define TEST_SEGMENT_STRIDE (10U)

/* 等待条件编码(信捷):高8位条件、低8位来源。 */
#define TEST_WAIT_CODE(condition, source) \
((uint16_t)(((uint16_t)(condition) << 8U) | (uint16_t)(source)))

typedef struct
{
uint16_t words[3][TEST_WORD_CAPACITY];
uint8_t bits[3][TEST_BIT_CAPACITY];
} TEST_MEMORY;

static int TestFailures;
static int TestChecks;

#define CHECK(condition) \
do \
{ \
TestChecks++; \
if (!(condition)) \
{ \
TestFailures++; \
(void)printf("FAIL line %d: %s\n", __LINE__, #condition); \
} \
} while (0)

static uint8_t TestValidateWords(void *context,
PLSR_DEVICE_TYPE device,
uint32_t firstAddress,
uint32_t wordCount)
{
(void)context;
if ((device > PLSR_DEVICE_FD) || (wordCount == 0UL))
{
return 0U;
}
return (((uint64_t)firstAddress + wordCount) <= TEST_WORD_CAPACITY)
? 1U
: 0U;
}

static uint8_t TestReadWord(void *context,
PLSR_DEVICE_TYPE device,
uint32_t address,
uint16_t *value)
{
TEST_MEMORY *memory = (TEST_MEMORY *)context;

if ((memory == NULL) || (value == NULL) || (device > PLSR_DEVICE_FD)
|| (address >= TEST_WORD_CAPACITY))
{
return 0U;
}
*value = memory->words[device][address];
return 1U;
}

static uint8_t TestReadBit(void *context,
PLSR_DEVICE_TYPE device,
uint32_t address,
uint8_t *value)
{
TEST_MEMORY *memory = (TEST_MEMORY *)context;
uint8_t index;

if ((memory == NULL) || (value == NULL) || (device < PLSR_DEVICE_X)
|| (device > PLSR_DEVICE_HM) || (address >= TEST_BIT_CAPACITY))
{
return 0U;
}
index = (uint8_t)(device - PLSR_DEVICE_X);
*value = memory->bits[index][address];
return 1U;
}

static void TestWriteDword(TEST_MEMORY *memory,
PLSR_DEVICE_TYPE device,
uint32_t address,
int32_t value)
{
uint32_t raw = (uint32_t)value;

memory->words[device][address] = (uint16_t)(raw & 0xFFFFUL);
memory->words[device][address + 1UL] = (uint16_t)(raw >> 16U);
}

/* 写入一个段的数据块(S0 布局,每段 10 字)。 */
static void TestSetSegment(TEST_MEMORY *memory,
uint16_t number,
uint32_t frequency,
int32_t pulses,
uint16_t waitCode,
int32_t waitValue,
uint16_t jumpSource,
int32_t jumpValue)
{
uint32_t base = TEST_S0_BASE + (uint32_t)number * TEST_SEGMENT_STRIDE;

TestWriteDword(memory, PLSR_DEVICE_D, base, (int32_t)frequency);
TestWriteDword(memory, PLSR_DEVICE_D, base + 2UL, pulses);
memory->words[PLSR_DEVICE_D][base + 4UL] = waitCode;
TestWriteDword(memory, PLSR_DEVICE_D, base + 5UL, waitValue);
memory->words[PLSR_DEVICE_D][base + 7UL] = (uint16_t)jumpSource;
TestWriteDword(memory, PLSR_DEVICE_D, base + 8UL, jumpValue);
}

static void TestResetEnvironment(void)
{
PlsrPersistenceTestResetStorage();
CHECK(PlcDeviceInit() == PLC_DEVICE_OK);
/* 默认 SFD + 配置方向端子(接线参数,测试用 Y4)。 */
CHECK(PlcDeviceWriteSfd(906U, 4) == PLC_DEVICE_OK);
CHECK(PlsrInit() == PLSR_RESULT_OK);
}

static PLSR_CALL TestMakeCall(TEST_MEMORY *memory)
{
PLSR_CALL call;

(void)memset(&call, 0, sizeof(call));
call.sequence = 10UL;
call.source.context = memory;
call.source.validateWords = TestValidateWords;
call.source.readWord = TestReadWord;
call.source.readBit = TestReadBit;
call.s0.device = PLSR_DEVICE_D;
call.s0.address = TEST_S0_BASE;
call.s1.device = PLSR_DEVICE_D;
call.s1.address = TEST_S1_BASE;
call.s2.type = PLSR_OPERAND_CONSTANT;
call.s2.constant = 1;
call.dAxis = 0U;
call.outputModeOverride = PLSR_OUTPUT_MODE_FROM_SFD;
return call;
}

static PLSR_STATUS TestGetStatus(void)
{
PLSR_STATUS status;

(void)memset(&status, 0, sizeof(status));
CHECK(PlsrGetStatus(0U, &status) == PLSR_RESULT_OK);
return status;
}

/* 当前段加速完成进入 RUN。 */
static void TestAccelToRun(void)
{
CHECK(PlsrPostEvent(0U, PLSR_EVENT_ACCEL_COMPLETE) == PLSR_RESULT_OK);
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_RUN);
}

/* 启动一个 N 段任务(全部 H00 完成条件、顺序跳转)。 */
static void TestStartSequentialJob(TEST_MEMORY *memory,
uint16_t segmentCount,
uint32_t sequence)
{
uint16_t segment;

(void)memset(memory, 0, sizeof(*memory));
TestWriteDword(memory, PLSR_DEVICE_D, TEST_S0_BASE, segmentCount);
for (segment = 1U; segment <= segmentCount; segment++)
{
TestSetSegment(memory,
segment,
1000U + (uint32_t)segment * 1000U,
(int32_t)(segment * 100),
TEST_WAIT_CODE(0U, 0U),
0,
0U,
0);
}
{
PLSR_CALL call = TestMakeCall(memory);

call.sequence = sequence;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
}
PlsrProcess();
CHECK(TestGetStatus().lastCommandResult == PLSR_RESULT_OK);
}

static void TestSequentialSegments(void)
{
TEST_MEMORY memory;

TestResetEnvironment();
TestStartSequentialJob(&memory, 3U, 10UL);
CHECK(TestGetStatus().state == PLSR_STATE_ACCEL);
CHECK(TestGetStatus().currentSegment == 1U);

TestAccelToRun();
CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK);
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_ACCEL);
CHECK(TestGetStatus().currentSegment == 2U);

TestAccelToRun();
CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK);
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_ACCEL);
CHECK(TestGetStatus().currentSegment == 3U);

TestAccelToRun();
CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK);
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_COMPLETED);
CHECK(TestGetStatus().done != 0U);
}

static void TestJumpToSpecifiedSegment(void)
{
TEST_MEMORY memory;

TestResetEnvironment();
(void)memset(&memory, 0, sizeof(memory));
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 3);
TestSetSegment(&memory, 1U, 1000U, 100, TEST_WAIT_CODE(0U, 0U), 0, 0U, 3);
TestSetSegment(&memory, 2U, 2000U, 200, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0);
TestSetSegment(&memory, 3U, 3000U, 300, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0);
{
PLSR_CALL call = TestMakeCall(&memory);

call.sequence = 20UL;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
}
PlsrProcess();
CHECK(TestGetStatus().currentSegment == 1U);

/* 段1 完成 → 跳到段3(跳过段2)。 */
TestAccelToRun();
CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK);
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_ACCEL);
CHECK(TestGetStatus().currentSegment == 3U);

/* 段3 完成 → 顺序下一段超出 → 任务结束。 */
TestAccelToRun();
CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK);
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_COMPLETED);
}

static void TestWaitTime(void)
{
TEST_MEMORY memory;
int ticks;

TestResetEnvironment();
(void)memset(&memory, 0, sizeof(memory));
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 2);
TestSetSegment(&memory, 1U, 1000U, 100,
TEST_WAIT_CODE(1U, 0U), 200, 0U, 0);
TestSetSegment(&memory, 2U, 2000U, 200, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0);
{
PLSR_CALL call = TestMakeCall(&memory);

call.sequence = 30UL;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
}
PlsrProcess();
TestAccelToRun();

/* 段1 完成 → 进入 WAIT(等 200ms)。 */
CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK);
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_WAIT);

/* 事件处理同轮已递减 1ms:198 个 tick 后仍在等待。 */
for (ticks = 0; ticks < 198; ticks++)
{
PlsrProcess();
}
CHECK(TestGetStatus().state == PLSR_STATE_WAIT);
CHECK(TestGetStatus().currentSegment == 1U);

/* 第 200 个 tick:计时到,跳到段2。 */
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_ACCEL);
CHECK(TestGetStatus().currentSegment == 2U);
}

static void TestWaitSignal(void)
{
TEST_MEMORY memory;

TestResetEnvironment();
(void)memset(&memory, 0, sizeof(memory));
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 2);
TestSetSegment(&memory, 1U, 1000U, 100,
TEST_WAIT_CODE(2U, 5U), 10, 0U, 0);
TestSetSegment(&memory, 2U, 2000U, 200, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0);
{
PLSR_CALL call = TestMakeCall(&memory);

call.sequence = 40UL;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
}
PlsrProcess();
TestAccelToRun();

/* 段1 完成 → 等 M10(当前 OFF)。 */
CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK);
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_WAIT);

/* 信号仍 OFF:不跳转。 */
PlsrProcess();
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_WAIT);

/* 信号置 ON:tick 轮询到后跳段。 */
memory.bits[1U][10U] = 1U; /* M10 */
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_ACCEL);
CHECK(TestGetStatus().currentSegment == 2U);
}

static void TestActTruncatesSegment(void)
{
TEST_MEMORY memory;
int ticks;

TestResetEnvironment();
(void)memset(&memory, 0, sizeof(memory));
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 2);
TestSetSegment(&memory, 1U, 1000U, 100,
TEST_WAIT_CODE(3U, 0U), 200, 0U, 0);
TestSetSegment(&memory, 2U, 2000U, 200, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0);
{
PLSR_CALL call = TestMakeCall(&memory);

call.sequence = 50UL;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
}
PlsrProcess();
TestAccelToRun();

/* 不注入 SEGMENT_COMPLETE:ACT 200ms 到时截断当前段。 */
for (ticks = 0; ticks < 200; ticks++)
{
PlsrProcess();
}
CHECK(TestGetStatus().state == PLSR_STATE_ACCEL);
CHECK(TestGetStatus().currentSegment == 2U);
}

static void TestActPendingAfterSegmentDone(void)
{
TEST_MEMORY memory;
int ticks;

TestResetEnvironment();
(void)memset(&memory, 0, sizeof(memory));
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 2);
TestSetSegment(&memory, 1U, 1000U, 100,
TEST_WAIT_CODE(3U, 0U), 200, 0U, 0);
TestSetSegment(&memory, 2U, 2000U, 200, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0);
{
PLSR_CALL call = TestMakeCall(&memory);

call.sequence = 60UL;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
}
PlsrProcess();
TestAccelToRun();

/* 段脉冲先完成(ACT 还剩 199ms)→ 停在 ACT_PENDING(WAIT 状态)。 */
CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK);
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_WAIT);

/* ACT 到时 → 跳段2。 */
for (ticks = 0; ticks < 199; ticks++)
{
PlsrProcess();
}
CHECK(TestGetStatus().state == PLSR_STATE_ACCEL);
CHECK(TestGetStatus().currentSegment == 2U);
}

static void TestExtEdgeTruncates(void)
{
TEST_MEMORY memory;
int ticks;

TestResetEnvironment();
(void)memset(&memory, 0, sizeof(memory));
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 2);
TestSetSegment(&memory, 1U, 1000U, 100,
TEST_WAIT_CODE(4U, 4U), 0, 0U, 0);
TestSetSegment(&memory, 2U, 2000U, 200, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0);
/* X0 在任务开始前就有效:进入段时记录为已有电平,不算新边沿。 */
memory.bits[0U][0U] = 1U;
{
PLSR_CALL call = TestMakeCall(&memory);

call.sequence = 70UL;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
}
PlsrProcess();
TestAccelToRun();

/* X0 持续 ON:没有新边沿,不应触发。 */
for (ticks = 0; ticks < 5; ticks++)
{
PlsrProcess();
}
CHECK(TestGetStatus().state == PLSR_STATE_RUN);
CHECK(TestGetStatus().currentSegment == 1U);

/* 先拉低再拉高:新上升沿,截断当前段。 */
memory.bits[0U][0U] = 0U;
PlsrProcess();
memory.bits[0U][0U] = 1U;
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_ACCEL);
CHECK(TestGetStatus().currentSegment == 2U);
}

static void TestExtOrComplete(void)
{
TEST_MEMORY memory;

TestResetEnvironment();
(void)memset(&memory, 0, sizeof(memory));
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 2);
TestSetSegment(&memory, 1U, 1000U, 100,
TEST_WAIT_CODE(5U, 4U), 0, 0U, 0);
TestSetSegment(&memory, 2U, 2000U, 200, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0);
{
PLSR_CALL call = TestMakeCall(&memory);

call.sequence = 80UL;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
}
PlsrProcess();
TestAccelToRun();

/* 段脉冲先完成 → 正常跳段(EXT 或完成)。 */
CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK);
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_ACCEL);
CHECK(TestGetStatus().currentSegment == 2U);
}

static void TestDynamicJump(void)
{
TEST_MEMORY memory;

TestResetEnvironment();
(void)memset(&memory, 0, sizeof(memory));
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 3);
TestSetSegment(&memory, 1U, 1000U, 100,
TEST_WAIT_CODE(0U, 0U), 0, 0U, 3);
TestSetSegment(&memory, 2U, 2000U, 200, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0);
/* 段3 跳转也来自 D300(初始 3 = 自跳转,运行期改非法值验证越界)。 */
TestSetSegment(&memory, 3U, 3000U, 300,
TEST_WAIT_CODE(0U, 0U), 0, 1U, 300);
TestWriteDword(&memory, PLSR_DEVICE_D, 300U, 3);
{
PLSR_CALL call = TestMakeCall(&memory);

call.sequence = 90UL;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
}
PlsrProcess();
TestAccelToRun();

/* 段1 完成:跳到段3。 */
CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK);
PlsrProcess();
CHECK(TestGetStatus().currentSegment == 3U);

/* 段3 完成前把 D300 改成非法值 → 运行期错误 → ERROR。 */
TestAccelToRun();
TestWriteDword(&memory, PLSR_DEVICE_D, 300U, 99);
CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK);
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_ERROR);
}

static void TestAbsoluteZeroDisplacement(void)
{
TEST_MEMORY memory;
PLSR_COMMAND command;

TestResetEnvironment();
(void)memset(&memory, 0, sizeof(memory));
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 2);
/* S1+0 = 1:绝对模式。 */
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S1_BASE, 1);
/* 段1 目标 0(=当前位置 0,零位移);段2 目标 5000。 */
TestSetSegment(&memory, 1U, 1000U, 0,
TEST_WAIT_CODE(0U, 0U), 0, 0U, 0);
TestSetSegment(&memory, 2U, 2000U, 5000,
TEST_WAIT_CODE(0U, 0U), 0, 0U, 0);

/* 绝对定位需要 positionValid:先 SET_POSITION(0)。 */
command.sequence = 91UL;
command.axis = 0U;
command.opcode = PLSR_CMD_SET_POSITION;
command.argument = 0;
CHECK(PlsrPostCommand(&command) == PLSR_RESULT_QUEUED);
PlsrProcess();

{
PLSR_CALL call = TestMakeCall(&memory);

call.sequence = 92UL;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
}
PlsrProcess();
/* 起点段1 零位移:直接跳过,从段2 开始发脉冲。 */
CHECK(TestGetStatus().state == PLSR_STATE_ACCEL);
CHECK(TestGetStatus().currentSegment == 2U);
}

static void TestZeroJumpBudgetYields(void)
{
TEST_MEMORY memory;
int rounds;

TestResetEnvironment();
(void)memset(&memory, 0, sizeof(memory));
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 3);
/* 段1:零脉冲,跳到段2;段2:零脉冲,动态跳转(初始自跳转);段3:正常段。 */
TestSetSegment(&memory, 1U, 1000U, 0,
TEST_WAIT_CODE(0U, 0U), 0, 0U, 2);
TestSetSegment(&memory, 2U, 1000U, 0,
TEST_WAIT_CODE(0U, 0U), 0, 1U, 300);
TestSetSegment(&memory, 3U, 1000U, 100,
TEST_WAIT_CODE(0U, 0U), 0, 0U, 0);
TestWriteDword(&memory, PLSR_DEVICE_D, 300U, 2);
{
PLSR_CALL call = TestMakeCall(&memory);

call.sequence = 100UL;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
}
PlsrProcess();

/* 每轮 100 次预算:零脉冲自循环链停在段2 并让出,任务不结束。 */
CHECK(TestGetStatus().state == PLSR_STATE_ACCEL);
CHECK(TestGetStatus().currentSegment == 2U);
CHECK(TestGetStatus().done == 0U);
for (rounds = 0; rounds < 3; rounds++)
{
PlsrProcess();
}
CHECK(TestGetStatus().state == PLSR_STATE_ACCEL);
CHECK(TestGetStatus().currentSegment == 2U);
CHECK(TestGetStatus().done == 0U);

/* 运行中改动态跳转目标 → 链走向段3(有脉冲)→ 正常执行并完成。 */
TestWriteDword(&memory, PLSR_DEVICE_D, 300U, 3);
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_ACCEL);
CHECK(TestGetStatus().currentSegment == 3U);
TestAccelToRun();
CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK);
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_COMPLETED);
}

static void TestSelfLoopInterruptedByStop(void)
{
TEST_MEMORY memory;
PLSR_COMMAND command;

TestResetEnvironment();
(void)memset(&memory, 0, sizeof(memory));
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 1);
TestSetSegment(&memory, 1U, 1000U, 100,
TEST_WAIT_CODE(0U, 0U), 0, 0U, 1);
{
PLSR_CALL call = TestMakeCall(&memory);

call.sequence = 110UL;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
}
PlsrProcess();
TestAccelToRun();

/* 段1 完成 → 自跳转回段1。 */
CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK);
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_ACCEL);
CHECK(TestGetStatus().currentSegment == 1U);

/* STOP 可打断自循环。 */
command.sequence = 111UL;
command.axis = 0U;
command.opcode = PLSR_CMD_STOP_IMMEDIATE;
command.argument = 0;
CHECK(PlsrPostCommand(&command) == PLSR_RESULT_QUEUED);
PlsrProcess();
CHECK(PlsrPostEvent(0U, PLSR_EVENT_STOP_IMMEDIATE_DONE)
== PLSR_RESULT_OK);
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_STOPPED);
CHECK(TestGetStatus().done == 0U);
}

static void TestLastSegmentWaitTime(void)
{
TEST_MEMORY memory;
int ticks;

TestResetEnvironment();
(void)memset(&memory, 0, sizeof(memory));
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 2);
TestSetSegment(&memory, 1U, 1000U, 100, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0);
/* 最后一段用 WAIT 时间(信捷警告场景:验证不会卡死)。 */
TestSetSegment(&memory, 2U, 2000U, 200,
TEST_WAIT_CODE(1U, 0U), 100, 0U, 0);
{
PLSR_CALL call = TestMakeCall(&memory);

call.sequence = 120UL;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
}
PlsrProcess();
TestAccelToRun();
CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK);
PlsrProcess();
TestAccelToRun();

/* 段2(最后一段)完成 → 等待 100ms。 */
CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK);
PlsrProcess();
CHECK(TestGetStatus().state == PLSR_STATE_WAIT);

/* 计时到 → 任务正常结束。 */
for (ticks = 0; ticks < 100; ticks++)
{
PlsrProcess();
}
CHECK(TestGetStatus().state == PLSR_STATE_COMPLETED);
CHECK(TestGetStatus().done != 0U);
}

int main(void)
{
TestSequentialSegments();
TestJumpToSpecifiedSegment();
TestWaitTime();
TestWaitSignal();
TestActTruncatesSegment();
TestActPendingAfterSegmentDone();
TestExtEdgeTruncates();
TestExtOrComplete();
TestDynamicJump();
TestAbsoluteZeroDisplacement();
TestZeroJumpBudgetYields();
TestSelfLoopInterruptedByStop();
TestLastSegmentWaitTime();

if (TestFailures != 0)
{
(void)printf("FAIL: %d of %d PLSR path checks failed\n",
TestFailures,
TestChecks);
return 1;
}
(void)printf("PASS: %d PLSR path checks\n", TestChecks);
return 0;
}

+ 3413
- 0
tmp/manual_p11_97.txt
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Laddar…
Avbryt
Spara