| @@ -25,6 +25,7 @@ | |||
| #include "ucos_ii.h" | |||
| #include "modbus_rtu_slave.h" | |||
| #include "plc_device.h" | |||
| #include "plsr_core.h" | |||
| #include "stdio.h" | |||
| /* USER CODE END Includes */ | |||
| @@ -51,6 +52,7 @@ static volatile uint8_t ConnectFlag; | |||
| /* USER CODE BEGIN PV */ | |||
| static OS_STK AppTaskStartStk[APP_TASK_START_STK_SIZE]; | |||
| static OS_STK PlsrTaskStk[APP_TASK_PLSR_STK_SIZE]; | |||
| /* USER CODE END PV */ | |||
| /* Private function prototypes -----------------------------------------------*/ | |||
| @@ -165,12 +167,27 @@ int main(void) | |||
| { | |||
| Error_Handler(); | |||
| } | |||
| if (PlsrInit() != PLSR_RESULT_OK) | |||
| { | |||
| Error_Handler(); | |||
| } | |||
| /* USER CODE BEGIN 2 */ | |||
| INT8U osError; | |||
| OSInit(); | |||
| osError = OSTaskCreateExt(PlsrTask, 0, | |||
| &PlsrTaskStk[APP_TASK_PLSR_STK_SIZE - 1u], | |||
| APP_TASK_PLSR_PRIO, APP_TASK_PLSR_PRIO, | |||
| &PlsrTaskStk[0], APP_TASK_PLSR_STK_SIZE, 0, | |||
| (OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR)); | |||
| if (osError != OS_ERR_NONE) | |||
| { | |||
| Error_Handler(); | |||
| } | |||
| osError = OSTaskCreateExt(AppTaskStart, 0, | |||
| &AppTaskStartStk[APP_TASK_START_STK_SIZE - 1u], | |||
| APP_TASK_START_PRIO, APP_TASK_START_PRIO, | |||
| @@ -1272,12 +1272,27 @@ | |||
| <file> | |||
| <name>$PROJ_DIR$\..\PLSR\Inc\plc_device.h</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\PLSR\Inc\plsr_types.h</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\PLSR\Inc\plsr_resource.h</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\PLSR\Inc\plsr_core.h</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\PLSR\Src\plsr_persistence.c</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\PLSR\Src\plc_device.c</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\PLSR\Src\plsr_resource.c</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\PLSR\Src\plsr_core.c</name> | |||
| </file> | |||
| </group> | |||
| <group> | |||
| <name>Modbus</name> | |||
| @@ -7,6 +7,7 @@ | |||
| */ | |||
| #define APP_TASK_START_PRIO 5u | |||
| #define APP_TASK_TEST_PRIO 6u | |||
| #define APP_TASK_PLSR_PRIO 4u | |||
| /* | |||
| * 任务栈大小的单位是OS_STK元素。当前OS_STK为32位, | |||
| @@ -14,5 +15,6 @@ | |||
| */ | |||
| #define APP_TASK_START_STK_SIZE 256u | |||
| #define APP_TASK_TEST_STK_SIZE 256u | |||
| #define APP_TASK_PLSR_STK_SIZE 256u | |||
| #endif | |||
| @@ -0,0 +1,28 @@ | |||
| #ifndef PLSR_CORE_H | |||
| #define PLSR_CORE_H | |||
| #include "plsr_types.h" | |||
| #include <stdint.h> | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #endif | |||
| PLSR_RESULT PlsrInit(void); | |||
| void PlsrTask(void *argument); | |||
| void PlsrProcess(void); | |||
| PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request); | |||
| PLSR_RESULT PlsrPostCommand(const PLSR_COMMAND *command); | |||
| PLSR_RESULT PlsrPostEvent(uint8_t axis, uint32_t eventMask); | |||
| PLSR_RESULT PlsrGetStatus(uint8_t axis, PLSR_STATUS *status); | |||
| PLSR_RESULT PlsrStateTransition(uint8_t axis, | |||
| PLSR_STATE target, | |||
| PLSR_TRANSITION_REASON reason); | |||
| #ifdef __cplusplus | |||
| } | |||
| #endif | |||
| #endif /* PLSR_CORE_H */ | |||
| @@ -0,0 +1,46 @@ | |||
| #ifndef PLSR_RESOURCE_H | |||
| #define PLSR_RESOURCE_H | |||
| #include "plsr_types.h" | |||
| #include <stdint.h> | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #endif | |||
| typedef struct | |||
| { | |||
| uint8_t ownerAxis; | |||
| PLSR_OUTPUT_MODE outputMode; | |||
| uint8_t dAxis; | |||
| uint8_t directionPoint; | |||
| } PLSR_RESOURCE_REQUEST; | |||
| typedef struct | |||
| { | |||
| uint32_t outputMask; | |||
| uint8_t highMask; | |||
| uint8_t ownerAxis; | |||
| uint8_t directionPoint; | |||
| PLSR_OUTPUT_MODE outputMode; | |||
| uint8_t valid; | |||
| } PLSR_RESOURCE_LEASE; | |||
| typedef struct | |||
| { | |||
| uint32_t outputMask; | |||
| uint8_t highMask; | |||
| } PLSR_RESOURCE_STATUS; | |||
| void PlsrResourceInit(void); | |||
| PLSR_RESULT PlsrResourceReserve(const PLSR_RESOURCE_REQUEST *request, | |||
| PLSR_RESOURCE_LEASE *lease); | |||
| void PlsrResourceRelease(PLSR_RESOURCE_LEASE *lease); | |||
| void PlsrResourceGetStatus(PLSR_RESOURCE_STATUS *status); | |||
| uint8_t PlsrResourceCheckInvariant(void); | |||
| #ifdef __cplusplus | |||
| } | |||
| #endif | |||
| #endif /* PLSR_RESOURCE_H */ | |||
| @@ -0,0 +1,168 @@ | |||
| #ifndef PLSR_TYPES_H | |||
| #define PLSR_TYPES_H | |||
| #include <stdint.h> | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #endif | |||
| #define PLSR_COMMAND_QUEUE_DEPTH (16U) | |||
| #define PLSR_DIRECTION_POINT_NONE (0xFFU) | |||
| typedef enum | |||
| { | |||
| PLSR_STATE_UNINITIALIZED = 0, | |||
| PLSR_STATE_IDLE, | |||
| PLSR_STATE_ACCEL, | |||
| PLSR_STATE_RUN, | |||
| PLSR_STATE_DECEL, | |||
| PLSR_STATE_WAIT, | |||
| PLSR_STATE_PAUSED, | |||
| PLSR_STATE_COMPLETED, | |||
| PLSR_STATE_STOPPED, | |||
| PLSR_STATE_ERROR | |||
| } PLSR_STATE; | |||
| typedef enum | |||
| { | |||
| PLSR_OUTPUT_PULSE_DIR = 0, | |||
| PLSR_OUTPUT_AB, | |||
| PLSR_OUTPUT_CW_CCW | |||
| } PLSR_OUTPUT_MODE; | |||
| typedef enum | |||
| { | |||
| PLSR_CMD_START = 0, | |||
| PLSR_CMD_STOP_DECEL, | |||
| PLSR_CMD_STOP_IMMEDIATE, | |||
| PLSR_CMD_PAUSE, | |||
| PLSR_CMD_RESUME, | |||
| PLSR_CMD_SET_POSITION, | |||
| PLSR_CMD_CLEAR_POSITION, | |||
| PLSR_CMD_CLEAR_TOTAL, | |||
| PLSR_CMD_SAVE_CONFIG, | |||
| PLSR_CMD_LOAD_CONFIG, | |||
| PLSR_CMD_RESET_ERROR, | |||
| PLSR_CMD_SELF_TEST | |||
| } PLSR_COMMAND_OPCODE; | |||
| typedef enum | |||
| { | |||
| PLSR_RESULT_OK = 0, | |||
| PLSR_RESULT_QUEUED, | |||
| PLSR_RESULT_INVALID_ARGUMENT, | |||
| PLSR_RESULT_INVALID_AXIS, | |||
| PLSR_RESULT_INVALID_STATE, | |||
| PLSR_RESULT_QUEUE_FULL, | |||
| PLSR_RESULT_RESOURCE_CONFLICT, | |||
| PLSR_RESULT_INVALID_RESOURCE, | |||
| PLSR_RESULT_BUSY, | |||
| PLSR_RESULT_PERSISTENCE_ERROR, | |||
| PLSR_RESULT_NOT_SUPPORTED, | |||
| PLSR_RESULT_INTERNAL_ERROR | |||
| } PLSR_RESULT; | |||
| typedef enum | |||
| { | |||
| PLSR_ERROR_NONE = 0, | |||
| PLSR_ERROR_ILLEGAL_TRANSITION, | |||
| PLSR_ERROR_RESOURCE_CONFLICT, | |||
| PLSR_ERROR_INVALID_RESOURCE, | |||
| PLSR_ERROR_TIMER_FAULT, | |||
| PLSR_ERROR_COUNTER_FAULT, | |||
| PLSR_ERROR_LIMIT_POSITIVE, | |||
| PLSR_ERROR_LIMIT_NEGATIVE, | |||
| PLSR_ERROR_INTERNAL | |||
| } PLSR_ERROR; | |||
| typedef enum | |||
| { | |||
| PLSR_STOP_REASON_NONE = 0, | |||
| PLSR_STOP_REASON_NORMAL_COMPLETE, | |||
| PLSR_STOP_REASON_PAUSE, | |||
| PLSR_STOP_REASON_STOP_DECEL, | |||
| PLSR_STOP_REASON_STOP_IMMEDIATE, | |||
| PLSR_STOP_REASON_LIMIT_POSITIVE, | |||
| PLSR_STOP_REASON_LIMIT_NEGATIVE, | |||
| PLSR_STOP_REASON_SOFTWARE_EMERGENCY, | |||
| PLSR_STOP_REASON_FAULT | |||
| } PLSR_STOP_REASON; | |||
| typedef enum | |||
| { | |||
| PLSR_TRANSITION_INITIALIZED = 0, | |||
| PLSR_TRANSITION_START, | |||
| PLSR_TRANSITION_ACCEL_COMPLETE, | |||
| PLSR_TRANSITION_DECEL_REQUEST, | |||
| PLSR_TRANSITION_DECEL_COMPLETE, | |||
| PLSR_TRANSITION_WAIT_BEGIN, | |||
| PLSR_TRANSITION_WAIT_COMPLETE, | |||
| PLSR_TRANSITION_JOB_COMPLETE, | |||
| PLSR_TRANSITION_STOP, | |||
| PLSR_TRANSITION_FAULT, | |||
| PLSR_TRANSITION_RESET_ERROR | |||
| } PLSR_TRANSITION_REASON; | |||
| #define PLSR_EVENT_SOFTWARE_EMERGENCY (1UL << 0U) | |||
| #define PLSR_EVENT_LIMIT_POSITIVE (1UL << 1U) | |||
| #define PLSR_EVENT_LIMIT_NEGATIVE (1UL << 2U) | |||
| #define PLSR_EVENT_TIMER_FAULT (1UL << 3U) | |||
| #define PLSR_EVENT_COUNTER_FAULT (1UL << 4U) | |||
| #define PLSR_EVENT_STOP_IMMEDIATE_DONE (1UL << 5U) | |||
| #define PLSR_EVENT_ACCEL_COMPLETE (1UL << 8U) | |||
| #define PLSR_EVENT_DECEL_COMPLETE (1UL << 9U) | |||
| #define PLSR_EVENT_WAIT_BEGIN (1UL << 10U) | |||
| #define PLSR_EVENT_WAIT_COMPLETE (1UL << 11U) | |||
| #define PLSR_EVENT_JOB_COMPLETE (1UL << 12U) | |||
| #define PLSR_EVENT_ALL_MASK (0x00001F3FUL) | |||
| #define PLSR_EVENT_CRITICAL_MASK (PLSR_EVENT_SOFTWARE_EMERGENCY \ | |||
| | PLSR_EVENT_LIMIT_POSITIVE \ | |||
| | PLSR_EVENT_LIMIT_NEGATIVE \ | |||
| | PLSR_EVENT_TIMER_FAULT \ | |||
| | PLSR_EVENT_COUNTER_FAULT) | |||
| typedef struct | |||
| { | |||
| uint32_t sequence; | |||
| uint8_t axis; | |||
| PLSR_COMMAND_OPCODE opcode; | |||
| int64_t argument; | |||
| } PLSR_COMMAND; | |||
| typedef struct | |||
| { | |||
| uint32_t sequence; | |||
| uint8_t axis; | |||
| PLSR_OUTPUT_MODE outputMode; | |||
| uint8_t directionPoint; | |||
| uint8_t directionPositive; | |||
| } PLSR_START_REQUEST; | |||
| typedef struct | |||
| { | |||
| PLSR_STATE state; | |||
| PLSR_OUTPUT_MODE outputMode; | |||
| PLSR_ERROR error; | |||
| PLSR_STOP_REASON stopReason; | |||
| PLSR_RESULT lastCommandResult; | |||
| uint32_t lastCommandSequence; | |||
| uint32_t illegalTransitionCount; | |||
| uint32_t pendingEvents; | |||
| int64_t logicalPosition; | |||
| int64_t totalPulses; | |||
| uint8_t busy; | |||
| uint8_t pulseActive; | |||
| uint8_t done; | |||
| uint8_t wait; | |||
| uint8_t directionPositive; | |||
| uint8_t highResourceMask; | |||
| uint8_t directionPoint; | |||
| } PLSR_STATUS; | |||
| #ifdef __cplusplus | |||
| } | |||
| #endif | |||
| #endif /* PLSR_TYPES_H */ | |||
| @@ -0,0 +1,209 @@ | |||
| #include "plsr_resource.h" | |||
| #include "plsr_address_map.h" | |||
| #include <string.h> | |||
| #ifndef PLSR_HOST_TEST | |||
| #include "stm32f4xx.h" | |||
| #endif | |||
| #define PLSR_HIGH_OUTPUT_MASK (0x0000000FUL) | |||
| #define PLSR_VALID_OUTPUT_MASK (0x0013FCFFUL) | |||
| #define PLSR_OUTPUT_POINT_COUNT (21U) | |||
| #define PLSR_RESOURCE_OWNER_NONE (0xFFU) | |||
| static uint32_t PlsrUsedOutputMask; | |||
| static uint8_t PlsrUsedHighMask; | |||
| static uint8_t PlsrOutputOwners[PLSR_OUTPUT_POINT_COUNT]; | |||
| static uint32_t PlsrResourceEnterCritical(void) | |||
| { | |||
| #ifdef PLSR_HOST_TEST | |||
| return 0UL; | |||
| #else | |||
| uint32_t interruptState = __get_PRIMASK(); | |||
| __disable_irq(); | |||
| __DMB(); | |||
| return interruptState; | |||
| #endif | |||
| } | |||
| static void PlsrResourceExitCritical(uint32_t interruptState) | |||
| { | |||
| #ifdef PLSR_HOST_TEST | |||
| (void)interruptState; | |||
| #else | |||
| __DMB(); | |||
| if (interruptState == 0UL) | |||
| { | |||
| __enable_irq(); | |||
| } | |||
| #endif | |||
| } | |||
| static uint8_t PlsrResourceOutputPointIsValid(uint8_t point) | |||
| { | |||
| if (point >= PLSR_OUTPUT_POINT_COUNT) | |||
| { | |||
| return 0U; | |||
| } | |||
| return ((PLSR_VALID_OUTPUT_MASK & (1UL << point)) != 0UL) ? 1U : 0U; | |||
| } | |||
| void PlsrResourceInit(void) | |||
| { | |||
| uint32_t interruptState = PlsrResourceEnterCritical(); | |||
| PlsrUsedOutputMask = 0UL; | |||
| PlsrUsedHighMask = 0U; | |||
| (void)memset(PlsrOutputOwners, | |||
| PLSR_RESOURCE_OWNER_NONE, | |||
| sizeof(PlsrOutputOwners)); | |||
| PlsrResourceExitCritical(interruptState); | |||
| } | |||
| PLSR_RESULT PlsrResourceReserve(const PLSR_RESOURCE_REQUEST *request, | |||
| PLSR_RESOURCE_LEASE *lease) | |||
| { | |||
| uint32_t requestedOutputMask; | |||
| uint32_t interruptState; | |||
| uint8_t requestedHighMask; | |||
| uint8_t point; | |||
| if ((request == NULL) || (lease == NULL)) | |||
| { | |||
| return PLSR_RESULT_INVALID_ARGUMENT; | |||
| } | |||
| if ((request->ownerAxis >= PLSR_AXIS_COUNT) | |||
| || (request->dAxis >= PLSR_AXIS_COUNT) | |||
| || (request->ownerAxis != request->dAxis)) | |||
| { | |||
| return PLSR_RESULT_INVALID_AXIS; | |||
| } | |||
| if (lease->valid != 0U) | |||
| { | |||
| return PLSR_RESULT_BUSY; | |||
| } | |||
| if (request->outputMode == PLSR_OUTPUT_PULSE_DIR) | |||
| { | |||
| if ((PlsrResourceOutputPointIsValid(request->directionPoint) == 0U) | |||
| || (request->directionPoint == request->dAxis)) | |||
| { | |||
| return PLSR_RESULT_INVALID_RESOURCE; | |||
| } | |||
| requestedHighMask = (uint8_t)(1U << request->dAxis); | |||
| requestedOutputMask = (uint32_t)requestedHighMask | |||
| | (1UL << request->directionPoint); | |||
| } | |||
| else if ((request->outputMode == PLSR_OUTPUT_AB) | |||
| || (request->outputMode == PLSR_OUTPUT_CW_CCW)) | |||
| { | |||
| if ((request->dAxis != 0U) && (request->dAxis != 2U)) | |||
| { | |||
| return PLSR_RESULT_INVALID_RESOURCE; | |||
| } | |||
| requestedHighMask = (uint8_t)(0x03U << request->dAxis); | |||
| requestedOutputMask = (uint32_t)requestedHighMask; | |||
| } | |||
| else | |||
| { | |||
| return PLSR_RESULT_INVALID_RESOURCE; | |||
| } | |||
| interruptState = PlsrResourceEnterCritical(); | |||
| if ((PlsrUsedOutputMask & requestedOutputMask) != 0UL) | |||
| { | |||
| PlsrResourceExitCritical(interruptState); | |||
| return PLSR_RESULT_RESOURCE_CONFLICT; | |||
| } | |||
| PlsrUsedOutputMask |= requestedOutputMask; | |||
| PlsrUsedHighMask |= requestedHighMask; | |||
| for (point = 0U; point < PLSR_OUTPUT_POINT_COUNT; point++) | |||
| { | |||
| if ((requestedOutputMask & (1UL << point)) != 0UL) | |||
| { | |||
| PlsrOutputOwners[point] = request->ownerAxis; | |||
| } | |||
| } | |||
| PlsrResourceExitCritical(interruptState); | |||
| lease->outputMask = requestedOutputMask; | |||
| lease->highMask = requestedHighMask; | |||
| lease->ownerAxis = request->ownerAxis; | |||
| lease->directionPoint = (request->outputMode == PLSR_OUTPUT_PULSE_DIR) | |||
| ? request->directionPoint | |||
| : PLSR_DIRECTION_POINT_NONE; | |||
| lease->outputMode = request->outputMode; | |||
| lease->valid = 1U; | |||
| return PLSR_RESULT_OK; | |||
| } | |||
| void PlsrResourceRelease(PLSR_RESOURCE_LEASE *lease) | |||
| { | |||
| uint32_t releaseMask = 0UL; | |||
| uint32_t interruptState; | |||
| uint8_t point; | |||
| if ((lease == NULL) || (lease->valid == 0U)) | |||
| { | |||
| return; | |||
| } | |||
| interruptState = PlsrResourceEnterCritical(); | |||
| for (point = 0U; point < PLSR_OUTPUT_POINT_COUNT; point++) | |||
| { | |||
| if (((lease->outputMask & (1UL << point)) != 0UL) | |||
| && (PlsrOutputOwners[point] == lease->ownerAxis)) | |||
| { | |||
| releaseMask |= (1UL << point); | |||
| PlsrOutputOwners[point] = PLSR_RESOURCE_OWNER_NONE; | |||
| } | |||
| } | |||
| PlsrUsedOutputMask &= ~releaseMask; | |||
| PlsrUsedHighMask = (uint8_t)(PlsrUsedOutputMask & PLSR_HIGH_OUTPUT_MASK); | |||
| PlsrResourceExitCritical(interruptState); | |||
| (void)memset(lease, 0, sizeof(*lease)); | |||
| lease->directionPoint = PLSR_DIRECTION_POINT_NONE; | |||
| } | |||
| void PlsrResourceGetStatus(PLSR_RESOURCE_STATUS *status) | |||
| { | |||
| uint32_t interruptState; | |||
| if (status == NULL) | |||
| { | |||
| return; | |||
| } | |||
| interruptState = PlsrResourceEnterCritical(); | |||
| status->outputMask = PlsrUsedOutputMask; | |||
| status->highMask = PlsrUsedHighMask; | |||
| PlsrResourceExitCritical(interruptState); | |||
| } | |||
| uint8_t PlsrResourceCheckInvariant(void) | |||
| { | |||
| uint32_t interruptState; | |||
| uint8_t valid = 1U; | |||
| uint8_t point; | |||
| interruptState = PlsrResourceEnterCritical(); | |||
| if (PlsrUsedHighMask | |||
| != (uint8_t)(PlsrUsedOutputMask & PLSR_HIGH_OUTPUT_MASK)) | |||
| { | |||
| valid = 0U; | |||
| } | |||
| for (point = 0U; point < PLSR_OUTPUT_POINT_COUNT; point++) | |||
| { | |||
| if (((PlsrUsedOutputMask & (1UL << point)) == 0UL) | |||
| != (PlsrOutputOwners[point] == PLSR_RESOURCE_OWNER_NONE)) | |||
| { | |||
| valid = 0U; | |||
| } | |||
| } | |||
| PlsrResourceExitCritical(interruptState); | |||
| return valid; | |||
| } | |||
| @@ -7,45 +7,64 @@ if ($null -eq $compiler) | |||
| throw 'gcc.exe was not found; PLSR host tests cannot run.' | |||
| } | |||
| $outputPath = Join-Path $workspacePath 'tmp\test_plc_device.exe' | |||
| $outputDirectory = Split-Path -Parent $outputPath | |||
| $outputDirectory = Join-Path $workspacePath 'tmp' | |||
| if (-not (Test-Path -LiteralPath $outputDirectory)) | |||
| { | |||
| New-Item -ItemType Directory -Path $outputDirectory | Out-Null | |||
| } | |||
| $arguments = @( | |||
| '-std=c11', | |||
| '-Wall', | |||
| '-Wextra', | |||
| '-Werror', | |||
| '-DPLSR_HOST_TEST', | |||
| "-I$workspacePath\PLSR\Inc", | |||
| "$workspacePath\PLSR\Src\plc_device.c", | |||
| "$workspacePath\PLSR\Src\plsr_persistence.c", | |||
| "$workspacePath\PLSR\Test\test_plc_device.c", | |||
| '-o', | |||
| $outputPath | |||
| $tests = @( | |||
| @{ | |||
| Name = 'test_plc_device' | |||
| Sources = @( | |||
| "$workspacePath\PLSR\Src\plc_device.c" | |||
| "$workspacePath\PLSR\Src\plsr_persistence.c" | |||
| "$workspacePath\PLSR\Test\test_plc_device.c" | |||
| ) | |||
| }, | |||
| @{ | |||
| Name = 'test_plsr_core' | |||
| Sources = @( | |||
| "$workspacePath\PLSR\Src\plc_device.c" | |||
| "$workspacePath\PLSR\Src\plsr_persistence.c" | |||
| "$workspacePath\PLSR\Src\plsr_resource.c" | |||
| "$workspacePath\PLSR\Src\plsr_core.c" | |||
| "$workspacePath\PLSR\Test\test_plsr_core.c" | |||
| ) | |||
| } | |||
| ) | |||
| try | |||
| foreach ($test in $tests) | |||
| { | |||
| & $compiler.Source @arguments | |||
| if ($LASTEXITCODE -ne 0) | |||
| { | |||
| throw "PLSR host-test compilation failed with exit code $LASTEXITCODE." | |||
| } | |||
| $outputPath = Join-Path $outputDirectory "$($test.Name).exe" | |||
| $arguments = @( | |||
| '-std=c11' | |||
| '-Wall' | |||
| '-Wextra' | |||
| '-Werror' | |||
| '-DPLSR_HOST_TEST' | |||
| "-I$workspacePath\PLSR\Inc" | |||
| ) + $test.Sources + @('-o', $outputPath) | |||
| & $outputPath | |||
| if ($LASTEXITCODE -ne 0) | |||
| try | |||
| { | |||
| throw "PLSR host tests failed with exit code $LASTEXITCODE." | |||
| & $compiler.Source @arguments | |||
| if ($LASTEXITCODE -ne 0) | |||
| { | |||
| throw "$($test.Name) compilation failed with exit code $LASTEXITCODE." | |||
| } | |||
| & $outputPath | |||
| if ($LASTEXITCODE -ne 0) | |||
| { | |||
| throw "$($test.Name) failed with exit code $LASTEXITCODE." | |||
| } | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| if (Test-Path -LiteralPath $outputPath) | |||
| finally | |||
| { | |||
| Remove-Item -LiteralPath $outputPath -Force | |||
| if (Test-Path -LiteralPath $outputPath) | |||
| { | |||
| Remove-Item -LiteralPath $outputPath -Force | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,356 @@ | |||
| #include "plc_device.h" | |||
| #include "plsr_core.h" | |||
| #include "plsr_persistence.h" | |||
| #include "plsr_resource.h" | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| static unsigned int TestCount; | |||
| #define TEST_CHECK(condition) \ | |||
| do \ | |||
| { \ | |||
| TestCount++; \ | |||
| if (!(condition)) \ | |||
| { \ | |||
| (void)fprintf(stderr, "FAIL line %d: %s\n", __LINE__, #condition); \ | |||
| exit(EXIT_FAILURE); \ | |||
| } \ | |||
| } while (0) | |||
| static void TestReset(void) | |||
| { | |||
| PlsrPersistenceTestResetStorage(); | |||
| TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); | |||
| TEST_CHECK(PlsrInit() == PLSR_RESULT_OK); | |||
| } | |||
| static PLSR_RESULT TestPostCommand(uint32_t sequence, | |||
| uint8_t axis, | |||
| PLSR_COMMAND_OPCODE opcode, | |||
| int64_t argument) | |||
| { | |||
| PLSR_COMMAND command; | |||
| command.sequence = sequence; | |||
| command.axis = axis; | |||
| command.opcode = opcode; | |||
| command.argument = argument; | |||
| return PlsrPostCommand(&command); | |||
| } | |||
| static PLSR_RESULT TestPostStart(uint32_t sequence, | |||
| uint8_t axis, | |||
| PLSR_OUTPUT_MODE mode, | |||
| uint8_t directionPoint, | |||
| uint8_t directionPositive) | |||
| { | |||
| PLSR_START_REQUEST request; | |||
| request.sequence = sequence; | |||
| request.axis = axis; | |||
| request.outputMode = mode; | |||
| request.directionPoint = directionPoint; | |||
| request.directionPositive = directionPositive; | |||
| return PlsrPostStart(&request); | |||
| } | |||
| static PLSR_STATUS TestGetStatus(uint8_t axis) | |||
| { | |||
| PLSR_STATUS status; | |||
| (void)memset(&status, 0, sizeof(status)); | |||
| TEST_CHECK(PlsrGetStatus(axis, &status) == PLSR_RESULT_OK); | |||
| return status; | |||
| } | |||
| static void TestResourceManager(void) | |||
| { | |||
| PLSR_RESOURCE_REQUEST request; | |||
| PLSR_RESOURCE_LEASE first; | |||
| PLSR_RESOURCE_LEASE second; | |||
| PLSR_RESOURCE_STATUS status; | |||
| (void)memset(&first, 0, sizeof(first)); | |||
| (void)memset(&second, 0, sizeof(second)); | |||
| PlsrResourceInit(); | |||
| request.ownerAxis = 0U; | |||
| request.outputMode = PLSR_OUTPUT_PULSE_DIR; | |||
| request.dAxis = 0U; | |||
| request.directionPoint = 4U; | |||
| TEST_CHECK(PlsrResourceReserve(&request, &first) == PLSR_RESULT_OK); | |||
| TEST_CHECK(first.highMask == 0x01U); | |||
| TEST_CHECK(first.outputMask == ((1UL << 0U) | (1UL << 4U))); | |||
| request.ownerAxis = 1U; | |||
| request.dAxis = 1U; | |||
| request.directionPoint = 4U; | |||
| TEST_CHECK(PlsrResourceReserve(&request, &second) | |||
| == PLSR_RESULT_RESOURCE_CONFLICT); | |||
| TEST_CHECK(second.valid == 0U); | |||
| request.directionPoint = 8U; | |||
| TEST_CHECK(PlsrResourceReserve(&request, &second) | |||
| == PLSR_RESULT_INVALID_RESOURCE); | |||
| request.directionPoint = 1U; | |||
| TEST_CHECK(PlsrResourceReserve(&request, &second) | |||
| == PLSR_RESULT_INVALID_RESOURCE); | |||
| PlsrResourceRelease(&first); | |||
| PlsrResourceRelease(&first); | |||
| request.ownerAxis = 0U; | |||
| request.outputMode = PLSR_OUTPUT_AB; | |||
| request.dAxis = 0U; | |||
| request.directionPoint = PLSR_DIRECTION_POINT_NONE; | |||
| TEST_CHECK(PlsrResourceReserve(&request, &first) == PLSR_RESULT_OK); | |||
| TEST_CHECK(first.highMask == 0x03U); | |||
| request.ownerAxis = 2U; | |||
| request.outputMode = PLSR_OUTPUT_PULSE_DIR; | |||
| request.dAxis = 2U; | |||
| request.directionPoint = 4U; | |||
| TEST_CHECK(PlsrResourceReserve(&request, &second) == PLSR_RESULT_OK); | |||
| TEST_CHECK(second.highMask == 0x04U); | |||
| request.ownerAxis = 1U; | |||
| request.outputMode = PLSR_OUTPUT_CW_CCW; | |||
| request.dAxis = 1U; | |||
| request.directionPoint = PLSR_DIRECTION_POINT_NONE; | |||
| PlsrResourceRelease(&second); | |||
| TEST_CHECK(PlsrResourceReserve(&request, &second) | |||
| == PLSR_RESULT_INVALID_RESOURCE); | |||
| PlsrResourceGetStatus(&status); | |||
| TEST_CHECK(status.highMask == 0x03U); | |||
| TEST_CHECK(PlsrResourceCheckInvariant() != 0U); | |||
| PlsrResourceRelease(&first); | |||
| PlsrResourceGetStatus(&status); | |||
| TEST_CHECK(status.outputMask == 0UL); | |||
| } | |||
| static void TestStateMachineAndCommands(void) | |||
| { | |||
| PLSR_RESOURCE_STATUS resources; | |||
| PLSR_STATUS status; | |||
| TestReset(); | |||
| status = TestGetStatus(0U); | |||
| TEST_CHECK(status.state == PLSR_STATE_IDLE); | |||
| TEST_CHECK(status.directionPoint == PLSR_DIRECTION_POINT_NONE); | |||
| TEST_CHECK(TestPostStart(1U, 0U, PLSR_OUTPUT_PULSE_DIR, 4U, 1U) | |||
| == PLSR_RESULT_QUEUED); | |||
| PlsrProcess(); | |||
| status = TestGetStatus(0U); | |||
| TEST_CHECK(status.state == PLSR_STATE_ACCEL); | |||
| TEST_CHECK(status.busy != 0U); | |||
| TEST_CHECK(status.pulseActive != 0U); | |||
| TEST_CHECK(status.done == 0U); | |||
| TEST_CHECK(status.highResourceMask == 0x01U); | |||
| TEST_CHECK(status.directionPoint == 4U); | |||
| TEST_CHECK(status.lastCommandSequence == 1U); | |||
| TEST_CHECK(status.lastCommandResult == PLSR_RESULT_OK); | |||
| TEST_CHECK(TestPostStart(1U, 0U, PLSR_OUTPUT_PULSE_DIR, 4U, 1U) | |||
| == PLSR_RESULT_OK); | |||
| TEST_CHECK(TestPostStart(1U, 1U, PLSR_OUTPUT_PULSE_DIR, 4U, 1U) | |||
| == PLSR_RESULT_QUEUED); | |||
| PlsrProcess(); | |||
| status = TestGetStatus(1U); | |||
| TEST_CHECK(status.state == PLSR_STATE_IDLE); | |||
| TEST_CHECK(status.lastCommandResult == PLSR_RESULT_RESOURCE_CONFLICT); | |||
| TEST_CHECK(status.error == PLSR_ERROR_RESOURCE_CONFLICT); | |||
| TEST_CHECK(PlsrPostEvent(0U, PLSR_EVENT_ACCEL_COMPLETE) | |||
| == PLSR_RESULT_OK); | |||
| PlsrProcess(); | |||
| TEST_CHECK(TestGetStatus(0U).state == PLSR_STATE_RUN); | |||
| TEST_CHECK(TestPostCommand(2U, 0U, PLSR_CMD_SET_POSITION, 500) | |||
| == PLSR_RESULT_QUEUED); | |||
| PlsrProcess(); | |||
| TEST_CHECK(TestGetStatus(0U).lastCommandResult == PLSR_RESULT_BUSY); | |||
| TEST_CHECK(TestPostCommand(3U, 0U, PLSR_CMD_PAUSE, 0) | |||
| == PLSR_RESULT_QUEUED); | |||
| PlsrProcess(); | |||
| TEST_CHECK(TestGetStatus(0U).state == PLSR_STATE_DECEL); | |||
| TEST_CHECK(PlsrPostEvent(0U, PLSR_EVENT_DECEL_COMPLETE) | |||
| == PLSR_RESULT_OK); | |||
| PlsrProcess(); | |||
| status = TestGetStatus(0U); | |||
| TEST_CHECK(status.state == PLSR_STATE_PAUSED); | |||
| TEST_CHECK(status.busy != 0U); | |||
| TEST_CHECK(status.pulseActive == 0U); | |||
| TEST_CHECK(status.highResourceMask == 0x01U); | |||
| TEST_CHECK(TestPostCommand(4U, 0U, PLSR_CMD_RESUME, 0) | |||
| == PLSR_RESULT_QUEUED); | |||
| PlsrProcess(); | |||
| TEST_CHECK(TestGetStatus(0U).state == PLSR_STATE_ACCEL); | |||
| TEST_CHECK(TestPostCommand(5U, 0U, PLSR_CMD_STOP_IMMEDIATE, 0) | |||
| == PLSR_RESULT_QUEUED); | |||
| PlsrProcess(); | |||
| status = TestGetStatus(0U); | |||
| TEST_CHECK(status.state == PLSR_STATE_ACCEL); | |||
| TEST_CHECK(status.stopReason == PLSR_STOP_REASON_STOP_IMMEDIATE); | |||
| TEST_CHECK(status.highResourceMask == 0x01U); | |||
| TEST_CHECK(TestPostCommand(5U, 0U, PLSR_CMD_STOP_IMMEDIATE, 0) | |||
| == PLSR_RESULT_OK); | |||
| TEST_CHECK(PlsrPostEvent(0U, PLSR_EVENT_STOP_IMMEDIATE_DONE) | |||
| == PLSR_RESULT_OK); | |||
| PlsrProcess(); | |||
| status = TestGetStatus(0U); | |||
| TEST_CHECK(status.state == PLSR_STATE_STOPPED); | |||
| TEST_CHECK(status.busy == 0U); | |||
| TEST_CHECK(status.done == 0U); | |||
| TEST_CHECK(status.highResourceMask == 0U); | |||
| PlsrResourceGetStatus(&resources); | |||
| TEST_CHECK(resources.outputMask == 0UL); | |||
| TEST_CHECK(TestPostStart(6U, 0U, PLSR_OUTPUT_PULSE_DIR, 4U, 0U) | |||
| == PLSR_RESULT_QUEUED); | |||
| PlsrProcess(); | |||
| TEST_CHECK(PlsrPostEvent(0U, PLSR_EVENT_JOB_COMPLETE) | |||
| == PLSR_RESULT_OK); | |||
| PlsrProcess(); | |||
| status = TestGetStatus(0U); | |||
| TEST_CHECK(status.state == PLSR_STATE_COMPLETED); | |||
| TEST_CHECK(status.done != 0U); | |||
| TEST_CHECK(status.stopReason == PLSR_STOP_REASON_NORMAL_COMPLETE); | |||
| TEST_CHECK(TestPostStart(7U, 0U, PLSR_OUTPUT_PULSE_DIR, 4U, 1U) | |||
| == PLSR_RESULT_QUEUED); | |||
| PlsrProcess(); | |||
| TEST_CHECK(TestGetStatus(0U).done == 0U); | |||
| TEST_CHECK(PlsrPostEvent(0U, | |||
| PLSR_EVENT_JOB_COMPLETE | |||
| | PLSR_EVENT_SOFTWARE_EMERGENCY) | |||
| == PLSR_RESULT_OK); | |||
| PlsrProcess(); | |||
| status = TestGetStatus(0U); | |||
| TEST_CHECK(status.state == PLSR_STATE_STOPPED); | |||
| TEST_CHECK(status.done == 0U); | |||
| TEST_CHECK(status.stopReason == PLSR_STOP_REASON_SOFTWARE_EMERGENCY); | |||
| TEST_CHECK(PlsrStateTransition(0U, | |||
| PLSR_STATE_RUN, | |||
| PLSR_TRANSITION_ACCEL_COMPLETE) | |||
| == PLSR_RESULT_INVALID_STATE); | |||
| status = TestGetStatus(0U); | |||
| TEST_CHECK(status.state == PLSR_STATE_ERROR); | |||
| TEST_CHECK(status.error == PLSR_ERROR_ILLEGAL_TRANSITION); | |||
| TEST_CHECK(status.illegalTransitionCount == 1UL); | |||
| TEST_CHECK(TestPostCommand(8U, 0U, PLSR_CMD_RESET_ERROR, 0) | |||
| == PLSR_RESULT_QUEUED); | |||
| PlsrProcess(); | |||
| TEST_CHECK(TestGetStatus(0U).state == PLSR_STATE_IDLE); | |||
| } | |||
| static void TestLimitWaitAndPairs(void) | |||
| { | |||
| PLSR_STATUS status; | |||
| TestReset(); | |||
| TEST_CHECK(TestPostStart(10U, 0U, PLSR_OUTPUT_PULSE_DIR, 4U, 1U) | |||
| == PLSR_RESULT_QUEUED); | |||
| PlsrProcess(); | |||
| TEST_CHECK(PlsrPostEvent(0U, | |||
| PLSR_EVENT_LIMIT_POSITIVE | |||
| | PLSR_EVENT_DECEL_COMPLETE) | |||
| == PLSR_RESULT_OK); | |||
| PlsrProcess(); | |||
| status = TestGetStatus(0U); | |||
| TEST_CHECK(status.state == PLSR_STATE_STOPPED); | |||
| TEST_CHECK(status.stopReason == PLSR_STOP_REASON_LIMIT_POSITIVE); | |||
| TEST_CHECK(status.error == PLSR_ERROR_LIMIT_POSITIVE); | |||
| TEST_CHECK(TestPostStart(11U, | |||
| 2U, | |||
| PLSR_OUTPUT_AB, | |||
| PLSR_DIRECTION_POINT_NONE, | |||
| 0U) | |||
| == PLSR_RESULT_QUEUED); | |||
| PlsrProcess(); | |||
| status = TestGetStatus(2U); | |||
| TEST_CHECK(status.highResourceMask == 0x0CU); | |||
| TEST_CHECK(status.outputMode == PLSR_OUTPUT_AB); | |||
| TEST_CHECK(PlsrPostEvent(2U, PLSR_EVENT_WAIT_BEGIN) == PLSR_RESULT_OK); | |||
| PlsrProcess(); | |||
| status = TestGetStatus(2U); | |||
| TEST_CHECK(status.state == PLSR_STATE_WAIT); | |||
| TEST_CHECK(status.busy != 0U); | |||
| TEST_CHECK(status.pulseActive == 0U); | |||
| TEST_CHECK(status.highResourceMask == 0x0CU); | |||
| TEST_CHECK(TestPostCommand(12U, 2U, PLSR_CMD_PAUSE, 0) | |||
| == PLSR_RESULT_QUEUED); | |||
| PlsrProcess(); | |||
| TEST_CHECK(TestGetStatus(2U).state == PLSR_STATE_PAUSED); | |||
| TEST_CHECK(TestPostCommand(13U, 2U, PLSR_CMD_STOP_DECEL, 0) | |||
| == PLSR_RESULT_QUEUED); | |||
| PlsrProcess(); | |||
| status = TestGetStatus(2U); | |||
| TEST_CHECK(status.state == PLSR_STATE_STOPPED); | |||
| TEST_CHECK(status.highResourceMask == 0U); | |||
| TEST_CHECK(TestPostStart(14U, | |||
| 1U, | |||
| PLSR_OUTPUT_CW_CCW, | |||
| PLSR_DIRECTION_POINT_NONE, | |||
| 1U) | |||
| == PLSR_RESULT_QUEUED); | |||
| PlsrProcess(); | |||
| status = TestGetStatus(1U); | |||
| TEST_CHECK(status.state == PLSR_STATE_IDLE); | |||
| TEST_CHECK(status.lastCommandResult == PLSR_RESULT_INVALID_RESOURCE); | |||
| } | |||
| static void TestQueueAndInputValidation(void) | |||
| { | |||
| PLSR_COMMAND command; | |||
| uint32_t sequence; | |||
| TestReset(); | |||
| TEST_CHECK(PlsrPostEvent(4U, PLSR_EVENT_JOB_COMPLETE) | |||
| == PLSR_RESULT_INVALID_AXIS); | |||
| TEST_CHECK(PlsrPostEvent(0U, 0UL) == PLSR_RESULT_INVALID_ARGUMENT); | |||
| TEST_CHECK(PlsrPostEvent(0U, 0x80000000UL) | |||
| == PLSR_RESULT_INVALID_ARGUMENT); | |||
| TEST_CHECK(PlsrGetStatus(0U, NULL) == PLSR_RESULT_INVALID_ARGUMENT); | |||
| TEST_CHECK(PlsrGetStatus(4U, NULL) == PLSR_RESULT_INVALID_AXIS); | |||
| command.axis = 1U; | |||
| command.opcode = PLSR_CMD_CLEAR_POSITION; | |||
| command.argument = 0; | |||
| for (sequence = 100U; | |||
| sequence < 100U + PLSR_COMMAND_QUEUE_DEPTH; | |||
| sequence++) | |||
| { | |||
| command.sequence = sequence; | |||
| TEST_CHECK(PlsrPostCommand(&command) == PLSR_RESULT_QUEUED); | |||
| } | |||
| command.sequence = 1000U; | |||
| TEST_CHECK(PlsrPostCommand(&command) == PLSR_RESULT_QUEUE_FULL); | |||
| PlsrProcess(); | |||
| TEST_CHECK(TestGetStatus(1U).lastCommandResult == PLSR_RESULT_OK); | |||
| TEST_CHECK(TestPostCommand(2000U, 0U, PLSR_CMD_SELF_TEST, 0) | |||
| == PLSR_RESULT_QUEUED); | |||
| PlsrProcess(); | |||
| TEST_CHECK(TestGetStatus(0U).lastCommandResult == PLSR_RESULT_OK); | |||
| } | |||
| int main(void) | |||
| { | |||
| TestResourceManager(); | |||
| TestStateMachineAndCommands(); | |||
| TestLimitWaitAndPairs(); | |||
| TestQueueAndInputValidation(); | |||
| (void)printf("PASS: %u PLSR core checks\n", TestCount); | |||
| return EXIT_SUCCESS; | |||
| } | |||