Initial MM32F527x commit.

Signed-off-by: Yilin Sun <imi415@imi.moe>
This commit is contained in:
Yilin Sun 2023-03-27 21:54:40 +08:00
commit 3977144e90
Signed by: imi415
GPG Key ID: 17F01E106F9F5E0A
4906 changed files with 1343450 additions and 0 deletions

291
components/at/at_server.c Normal file
View File

@ -0,0 +1,291 @@
/*
* Copyright 2022 MindMotion Microelectronics Co., Ltd.
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "at_server.h"
/*
* Macros.
*/
/*
* Declerations.
*/
/* at command parse. */
void at_parse(at_server_t * server);
/* get the at command name from at receive command buffer.*/
static bool at_cmd_get_name(char * cmd_buffer, char * cmd_name);
/* find the at command name from at command list. */
static at_cmd_t * at_find_cmd(at_server_t * server, char * cmd);
/* process the at command. */
static bool at_cmd_process(at_server_t * server, at_cmd_t * cmd, char * cmd_args);
/* check the ar command parameter format. */
static bool at_check_args(char * args, char * args_format);
/*
* Variables.
*/
/*
* Functions.
*/
void at_server_init(at_server_t * server,at_adapter_t * adapter)
{
adapter->init();
server->adapter = adapter;
server->remaining = adapter->buf_size;
memcpy(server->end_mark, AT_CMD_END_MARK_DEFAULT, sizeof(AT_CMD_END_MARK_DEFAULT));
}
void at_server_set_cmd_list(at_server_t * server, at_cmd_t * cmd_list[], uint32_t cmd_num)
{
server->cmd_list = cmd_list;
server->cmd_num = cmd_num;
}
void at_server_write(at_server_t * server, char * buf, uint32_t len)
{
server->adapter->write(buf, len);
}
uint32_t at_server_read(at_server_t * server, char * buf, uint32_t len)
{
return server->adapter->read(buf, len);
}
void at_server_task(at_server_t * server)
{
char * matchstr=NULL; /* Represents the string left after being returned using the strstr function. */
char * buf = &(server->adapter->buf[server->adapter->buf_size - server->remaining]);
uint32_t len = at_server_read(server, buf, server->remaining);
if (0u != len)
{
server->remaining -= len;
while(1) /* wait for all recipients in the received data to be compared. */
{
matchstr = strstr(server->adapter->buf, server->end_mark);
if (NULL != matchstr)
{
at_parse(server);
/* not copy the last matching terminator. */
memcpy(server->adapter->buf, matchstr+strlen(server->end_mark), strlen(server->adapter->buf));
server->remaining = server->adapter->buf_size - strlen(matchstr);
}
else
{
break;
}
}
}
}
/*parse the receive datas.*/
void at_parse(at_server_t * server)
{
char cur_cmd_name[AT_CMD_NAME_LEN] = { 0 };
at_cmd_t * cur_cmd = NULL;
char * cur_cmd_args = NULL;
char * buf = server->adapter->buf;
if (at_cmd_get_name(buf, cur_cmd_name) == false)
{
AT_LOG("ERROR CMD MATCH FAILED!\r\n");
return;
}
cur_cmd = at_find_cmd(server, cur_cmd_name);
if (cur_cmd == false)
{
AT_LOG("ERROR CMD MATCH FAILED!\r\n");
return;
}
cur_cmd_args = buf + strlen(cur_cmd_name);
if (at_cmd_process(server, cur_cmd, cur_cmd_args) == false)
{
AT_LOG("ERROR PARSE ARGS FAILED!\r\n");
return;
}
}
/* get at command name from at server receive buffer. */
static bool at_cmd_get_name(char * cmd_buffer, char * cmd_name)
{
uint32_t cmd_name_len = 0u;
for (uint32_t i = 0; i < strlen(cmd_buffer) + 1; i++)
{
if ( (*(cmd_buffer + i) == AT_CMD_QUESTION_MARK) || (*(cmd_buffer + i) == AT_CMD_EQUAL_MARK)
|| (*(cmd_buffer + i) == AT_CMD_CR)
|| ( (*(cmd_buffer + i) >= AT_CMD_CHAR_0) && (*(cmd_buffer + i) <= AT_CMD_CHAR_9) ) )
{
cmd_name_len = i;
memcpy(cmd_name, cmd_buffer, cmd_name_len);
*(cmd_name + cmd_name_len) = '\0';
return true;
}
}
return false;
}
/* find if the current at command name is in the at command list. */
static at_cmd_t * at_find_cmd(at_server_t * server, char * cmd)
{
for (uint32_t i = 0; i < server->cmd_num; i++)
{
if (!strcasecmp(cmd,server->cmd_list[i]->name))
{
return server->cmd_list[i];
}
}
return false;
}
/* parse command parameters and execute corresponding command functions. */
static bool at_cmd_process(at_server_t * server, at_cmd_t * cmd, char * cmd_args)
{
at_result_t result = AT_RESULT_OK;
if ( (cmd_args[0] == AT_CMD_EQUAL_MARK) && (cmd_args[1] == AT_CMD_QUESTION_MARK) && (cmd_args[2] == AT_CMD_CR) )
{
if (cmd->test == NULL)
{
AT_LOG("ERROR CHECK ARGS FORMAT FAILED!\r\n");
return false;
}
result = cmd->test(server->adapter->write);
}
else if ( (cmd_args[0] == AT_CMD_QUESTION_MARK) && (cmd_args[1] == AT_CMD_CR) )
{
if (cmd->query == NULL)
{
AT_LOG("ERROR CHECK ARGS FORMAT FAILED!\r\n");
return false;
}
result = cmd->query(server->adapter->write);
}
else if ( (cmd_args[0] == AT_CMD_EQUAL_MARK)
|| ( (cmd_args[0] >= AT_CMD_CHAR_0) && (cmd_args[0] <= AT_CMD_CHAR_9) && (cmd_args[1] == AT_CMD_CR) ) )
{
if (cmd->setup == NULL)
{
AT_LOG("ERROR CHECK ARGS FORMAT FAILED!\r\n");
return false;
}
if(at_check_args(cmd_args, cmd->args_expr) == false)
{
AT_LOG("ERROR CHECK ARGS FORMAT FAILED!\r\n");;
return false;
}
result = cmd->setup(server->adapter->write, cmd_args);
}
else if (cmd_args[0] == AT_CMD_CR)
{
if (cmd->exec == NULL)
{
return false;
}
result = cmd->exec(server->adapter->write);
}
else
{
return result;
}
return true;
}
/* check the ar command parameter format. */
static bool at_check_args(char * args, char * args_format)
{
uint32_t left_sq_bracket_num = 0, right_sq_bracket_num = 0;
uint32_t left_angle_bracket_num = 0, right_angle_bracket_num = 0;
uint32_t comma_mark_num = 0;
for (uint32_t i = 0; i < strlen(args_format); i++)
{
switch (args_format[i])
{
case AT_CMD_L_SQ_BRACKET:
left_sq_bracket_num++;
break;
case AT_CMD_R_SQ_BRACKET:
right_sq_bracket_num++;
break;
case AT_CMD_L_ANGLE_BRACKET:
left_angle_bracket_num++;
break;
case AT_CMD_R_ANGLE_BRACKET:
right_angle_bracket_num++;
break;
default:
break;
}
}
if ( (left_sq_bracket_num != right_sq_bracket_num) || (left_angle_bracket_num != right_angle_bracket_num)
|| (left_sq_bracket_num > left_angle_bracket_num) )
{
return -1;
}
for (uint32_t i = 0; i < strlen(args); i++)
{
if (args[i] == AT_CMD_COMMA_MARK)
{
comma_mark_num++;
}
}
if ( (comma_mark_num + 1 < left_angle_bracket_num - left_sq_bracket_num)
|| (comma_mark_num + 1 > left_angle_bracket_num) )
{
return false;
}
return true;
}
/* parse the number of arguments to the at request instruction. */
int at_req_parse_args(const char * req_args, const char * req_expr, ...)
{
va_list args;
int req_args_num = 0;
va_start(args, req_expr);
req_args_num = vsscanf(req_args, req_expr, args);
va_end(args);
return req_args_num;
}
/* EOF. */

186
components/at/at_server.h Normal file
View File

@ -0,0 +1,186 @@
/*
* Copyright 2022 MindMotion Microelectronics Co., Ltd.
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __AT_SERVER_H__
#define __AT_SERVER_H__
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
/*!
* @addtogroup AT
* @{
*/
/*!
* @brief AT component version number.
*/
#define AT_COMPONENT_VERSION 0u
/* AT LOG. */
/* when using AT_LOG, the user is required to implement printf function. */
#ifdef ENABLE_AT_LOG
#define AT_LOG(...) printf("[AT]:");printf(__VA_ARGS__);
#else
#define AT_LOG(...)
#endif
/*!
* @addtogroup AT_CMD_END_MARK
* define at command end mark.
* @{
*/
#define AT_CMD_END_MARK_DEFAULT "\r\n" /* the end mark of at command. */
#define AT_END_MARK_LEN 4u /* the length of at command end mark. */
#define AT_CMD_NAME_LEN 16u /* the length of at command name. */
/*!
* @}
*/
/*!
* @addtogroup AT_CMD_PARAM_FORMAT
* define the parameter format for at command.
* @{
*/
#define AT_CMD_CHAR_0 '0'
#define AT_CMD_CHAR_9 '9'
#define AT_CMD_QUESTION_MARK '?'
#define AT_CMD_EQUAL_MARK '='
#define AT_CMD_L_SQ_BRACKET '['
#define AT_CMD_R_SQ_BRACKET ']'
#define AT_CMD_L_ANGLE_BRACKET '<'
#define AT_CMD_R_ANGLE_BRACKET '>'
#define AT_CMD_COMMA_MARK ','
#define AT_CMD_SEMICOLON ';'
#define AT_CMD_CR '\r'
#define AT_CMD_LF '\n'
/*!
* @}
*/
/*!
* @brief at result type.
*
* at command parsing result.
*/
typedef enum
{
AT_RESULT_OK = 0, /* at command parsing result is no error */
AT_RESULT_FAILE = 1, /* at command parsing result have a generic error */
AT_RESULT_NULL = 2, /* at command parsing result not need return */
AT_RESULT_CMD_ERR = 3, /* at command format error or No way to execute */
AT_RESULT_CHECK_FAILE = 4, /* at command expression format is error */
AT_RESULT_PARSE_FAILE = 5, /* at command arguments parse is error */
} at_result_t;
/*!
* @brief define function pointer.
*/
typedef void (* at_server_init_t )(void);
typedef void (* at_server_write_t)(char * buf, uint32_t len);
typedef uint32_t (* at_server_read_t )(char * buf, uint32_t len);
typedef at_result_t (* at_server_test_t )(at_server_write_t write);
typedef at_result_t (* at_server_query_t)(at_server_write_t write);
typedef at_result_t (* at_server_setup_t)(at_server_write_t write, char * args);
typedef at_result_t (* at_server_exec_t )(at_server_write_t write);
/*!
* @brief This type of structure instance is used to keep the settings.
* when calling @ref at_server_init() to initialize the at adapter.
*/
typedef struct
{
at_server_init_t init; /* the at server initialization port. */
at_server_write_t write; /* the at adapter writer buffer port.*/
at_server_read_t read; /* the at adapter read buffer port. */
char * buf; /* the at adapter receive buffer. */
uint32_t buf_size; /* the at adapter receive buffer size. */
} at_adapter_t;
/*!
* @brief This type of structure instance is used to keep the settings.
* when calling @ref at_server_set_cmd_list() to set the at cmd lists.
*/
typedef struct
{
char * name; /* the pointer of at command name. */
char * args_expr; /* the pointer of at command parameter. */
at_server_test_t test; /* query the command parameter format and value range. */
at_server_query_t query; /* return the current value of the command parameter. */
at_server_setup_t setup; /* set the parameters specified by the user to the corresponding function. */
at_server_exec_t exec; /* execute related operations. */
} at_cmd_t;
/*!
* @brief the at server structure.
*/
typedef struct
{
at_adapter_t * adapter; /* the pointer of at adapter port. */
at_cmd_t ** cmd_list; /* the pointer of at command list. */
uint32_t cmd_num; /* the number of at command list. */
uint32_t remaining; /* the remaining of the receive buffer. */
char end_mark[AT_END_MARK_LEN]; /* the end mark of at command. */
} at_server_t;
/*!
* @brief Initialize the at server module.
*
* @param server Pointer to initialization at server structure. See to @ref at_server_t.
* @param adapter Pointer to the initialization at aadapter structure. See to @ref at_adapter_t.
* @return none.
*/
void at_server_init(at_server_t * server, at_adapter_t * adapter);
/*!
* @brief Set the at server command list.
*
* @param server at server instance.
* @param cmd_list set the at command list. See to @ref at_cmd_t.
* @param cmd_num the number of the at command.
* @return none.
*/
void at_server_set_cmd_list(at_server_t * server, at_cmd_t * cmd_list[], uint32_t cmd_num);
/*!
* @brief Set the at server task.
* at server start.
* @param server at server instance.
* @return none.
*/
void at_server_task(at_server_t * server);
/*!
* @brief at server write buffer.
*
* @param server at server instance.
* @param buf the data buffer.
* @param len the data buffer length.
* @return none.
*/
void at_server_write(at_server_t * server, char * buf, uint32_t len);
/*!
* @brief at server request arguments parse arguments.
*
* @param req_args request arguments.
* @param req_expr request expression.
* @return -1: parse arguments failed.
* 0: parse without match.
* >0: The number of arguments successfully paresd.
*/
int at_req_parse_args(const char * req_args, const char * req_expr, ...);
#endif /* __AT_SERVER_H__ */

53
components/at/readme.md Normal file
View File

@ -0,0 +1,53 @@
# AT Server
[TOC]
# Introduction
Mainly realize the following functions
- Parse the received AT command and respond
- User can customize AT command set
- Each AT command can realize four functions
| Function | Expression | Description |
| ---- | ---------- | -------------- |
| Test function | AT+<x>=? | Used to query the parameter format and value range |
| Query function | AT+<x>? | Used to return command parameters |
| Setting function | AT+<x>=... | For user-defined parameters |
| Execute function | AT+<x> | Used to perform related operations |
# Instructions for use
## Define AT adapter interface
Instantiate the **at_adapter_t** structure
```c
typedef struct{
at_server_init_t init; /* the at server initialization port. */
at_server_write_t write; /* the at adapter writer buffer port.*/
at_server_read_t read; /* the at adapter read buffer port. */
char * buf; /* the at adapter receive buffer. */
uint32_t buf_size; /* the at adapter receive buffer size. */
}at_adapter_t;
```
## Define AT command set
Create an array of AT command set structures and instantiate the **at_cmd_t** structure
```c
typedef struct{
char *name; /* the name of at command. */
char *args_expr; /* the parameter of at command. */
at_server_test_t test; /* to query the command parameter format and value range. */
at_server_query_t query; /* to return the current value of the command parameter. */
at_server_setup_t setup; /* for user-defined parameter value. */
at_server_exec_t exec; /* to execute related operations. */
}at_cmd_t;
```
## Initialize AT Server
The underlying interface are passed to the Server through the AT adapter. The corresponding APIs are **at_server_init()**。 And using the **at_server_set_cmd_list()** api to pass the at command list to the server.
## Start the AT Server task
Call the API **at_server_task()** to start the server and start working, waiting to receive commands and respond.

View File

@ -0,0 +1,953 @@
/*
* Main CANopen stack file. It combines Object dictionary (CO_OD) and all other
* CANopen source files. Configuration information are read from CO_OD.h file.
*
* @file CANopen.c
* @ingroup CO_CANopen
* @author Janez Paternoster
* @copyright 2010 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "CANopen.h"
/* If defined, global variables will be used, otherwise CANopen objects will
be generated with calloc(). */
/* #define CO_USE_GLOBALS */
/* If defined, the user provides an own implemetation for calculating the
* CRC16 CCITT checksum. */
/* #define CO_USE_OWN_CRC16 */
#ifndef CO_USE_GLOBALS
#include <stdlib.h> /* for malloc, free */
static uint32_t CO_memoryUsed = 0; /* informative */
#endif
/* Global variables ***********************************************************/
extern const CO_OD_entry_t CO_OD[CO_OD_NoOfElements]; /* Object Dictionary array */
static CO_t COO;
CO_t *CO = NULL;
static CO_CANrx_t *CO_CANmodule_rxArray0;
static CO_CANtx_t *CO_CANmodule_txArray0;
static CO_OD_extension_t *CO_SDO_ODExtensions;
static CO_HBconsNode_t *CO_HBcons_monitoredNodes;
#if CO_NO_TRACE > 0
static uint32_t *CO_traceTimeBuffers[CO_NO_TRACE];
static int32_t *CO_traceValueBuffers[CO_NO_TRACE];
#ifdef CO_USE_GLOBALS
#ifndef CO_TRACE_BUFFER_SIZE_FIXED
#define CO_TRACE_BUFFER_SIZE_FIXED 100
#endif
#endif
#endif
/* Verify features from CO_OD *************************************************/
/* generate error, if features are not correctly configured for this project */
#if CO_NO_NMT_MASTER > 1 \
|| CO_NO_SYNC > 1 \
|| CO_NO_EMERGENCY != 1 \
|| CO_NO_SDO_SERVER == 0 \
|| CO_NO_TIME > 1 \
|| CO_NO_SDO_CLIENT > 128 \
|| (CO_NO_RPDO < 1 || CO_NO_RPDO > 0x200) \
|| (CO_NO_TPDO < 1 || CO_NO_TPDO > 0x200) \
|| ODL_consumerHeartbeatTime_arrayLength == 0 \
|| ODL_errorStatusBits_stringLength < 10 \
|| CO_NO_LSS_SERVER > 1 \
|| CO_NO_LSS_CLIENT > 1 \
|| (CO_NO_LSS_SERVER > 0 && CO_NO_LSS_CLIENT > 0)
#error Features from CO_OD.h file are not corectly configured for this project!
#endif
/* Indexes for CANopenNode message objects ************************************/
#ifdef ODL_consumerHeartbeatTime_arrayLength
#define CO_NO_HB_CONS ODL_consumerHeartbeatTime_arrayLength
#else
#define CO_NO_HB_CONS 0
#endif
#define CO_NO_HB_PROD 1 /* Producer Heartbeat Cont */
#define CO_RXCAN_NMT 0 /* index for NMT message */
#define CO_RXCAN_SYNC 1 /* index for SYNC message */
#define CO_RXCAN_EMERG (CO_RXCAN_SYNC+CO_NO_SYNC) /* index for Emergency message */
#define CO_RXCAN_TIME (CO_RXCAN_EMERG+CO_NO_EMERGENCY) /* index for TIME message */
#define CO_RXCAN_RPDO (CO_RXCAN_TIME+CO_NO_TIME) /* start index for RPDO messages */
#define CO_RXCAN_SDO_SRV (CO_RXCAN_RPDO+CO_NO_RPDO) /* start index for SDO server message (request) */
#define CO_RXCAN_SDO_CLI (CO_RXCAN_SDO_SRV+CO_NO_SDO_SERVER) /* start index for SDO client message (response) */
#define CO_RXCAN_CONS_HB (CO_RXCAN_SDO_CLI+CO_NO_SDO_CLIENT) /* start index for Heartbeat Consumer messages */
#define CO_RXCAN_LSS (CO_RXCAN_CONS_HB+CO_NO_HB_CONS) /* index for LSS rx message */
/* total number of received CAN messages */
#define CO_RXCAN_NO_MSGS (\
1 + \
CO_NO_SYNC + \
CO_NO_EMERGENCY + \
CO_NO_TIME + \
CO_NO_RPDO + \
CO_NO_SDO_SERVER + \
CO_NO_SDO_CLIENT + \
CO_NO_HB_CONS + \
CO_NO_LSS_SERVER + \
CO_NO_LSS_CLIENT + \
0 \
)
#define CO_TXCAN_NMT 0 /* index for NMT master message */
#define CO_TXCAN_SYNC CO_TXCAN_NMT+CO_NO_NMT_MASTER /* index for SYNC message */
#define CO_TXCAN_EMERG (CO_TXCAN_SYNC+CO_NO_SYNC) /* index for Emergency message */
#define CO_TXCAN_TIME (CO_TXCAN_EMERG+CO_NO_EMERGENCY) /* index for TIME message */
#define CO_TXCAN_TPDO (CO_TXCAN_TIME+CO_NO_TIME) /* start index for TPDO messages */
#define CO_TXCAN_SDO_SRV (CO_TXCAN_TPDO+CO_NO_TPDO) /* start index for SDO server message (response) */
#define CO_TXCAN_SDO_CLI (CO_TXCAN_SDO_SRV+CO_NO_SDO_SERVER) /* start index for SDO client message (request) */
#define CO_TXCAN_HB (CO_TXCAN_SDO_CLI+CO_NO_SDO_CLIENT) /* index for Heartbeat message */
#define CO_TXCAN_LSS (CO_TXCAN_HB+CO_NO_HB_PROD) /* index for LSS tx message */
/* total number of transmitted CAN messages */
#define CO_TXCAN_NO_MSGS ( \
CO_NO_NMT_MASTER + \
CO_NO_SYNC + \
CO_NO_EMERGENCY + \
CO_NO_TIME + \
CO_NO_TPDO + \
CO_NO_SDO_SERVER + \
CO_NO_SDO_CLIENT + \
CO_NO_HB_PROD + \
CO_NO_LSS_SERVER + \
CO_NO_LSS_CLIENT + \
0\
)
#ifdef CO_USE_GLOBALS
static CO_CANmodule_t COO_CANmodule;
static CO_CANrx_t COO_CANmodule_rxArray0[CO_RXCAN_NO_MSGS];
static CO_CANtx_t COO_CANmodule_txArray0[CO_TXCAN_NO_MSGS];
static CO_SDO_t COO_SDO[CO_NO_SDO_SERVER];
static CO_OD_extension_t COO_SDO_ODExtensions[CO_OD_NoOfElements];
static CO_EM_t COO_EM;
static CO_EMpr_t COO_EMpr;
static CO_NMT_t COO_NMT;
#if CO_NO_SYNC == 1
static CO_SYNC_t COO_SYNC;
#endif
#if CO_NO_TIME == 1
static CO_TIME_t COO_TIME;
#endif
static CO_RPDO_t COO_RPDO[CO_NO_RPDO];
static CO_TPDO_t COO_TPDO[CO_NO_TPDO];
static CO_HBconsumer_t COO_HBcons;
static CO_HBconsNode_t COO_HBcons_monitoredNodes[CO_NO_HB_CONS];
#if CO_NO_LSS_SERVER == 1
static CO_LSSslave_t CO0_LSSslave;
#endif
#if CO_NO_LSS_CLIENT == 1
static CO_LSSmaster_t CO0_LSSmaster;
#endif
#if CO_NO_SDO_CLIENT != 0
static CO_SDOclient_t COO_SDOclient[CO_NO_SDO_CLIENT];
#endif
#if CO_NO_TRACE > 0
static CO_trace_t COO_trace[CO_NO_TRACE];
static uint32_t COO_traceTimeBuffers[CO_NO_TRACE][CO_TRACE_BUFFER_SIZE_FIXED];
static int32_t COO_traceValueBuffers[CO_NO_TRACE][CO_TRACE_BUFFER_SIZE_FIXED];
#endif
#endif
/* These declarations here are needed in the case the switches for the project
change the visibility in the headers in a way that the compiler doesn't see an declaration anymore */
#if CO_NO_LSS_SERVER == 0 /* LSS Server means LSS slave */
CO_ReturnError_t CO_new(void);
CO_ReturnError_t CO_CANinit(
void *CANdriverState,
uint16_t bitRate);
CO_ReturnError_t CO_LSSinit(
uint8_t nodeId,
uint16_t bitRate);
CO_ReturnError_t CO_CANopenInit(
uint8_t nodeId);
#else /* CO_NO_LSS_SERVER == 0 */
CO_ReturnError_t CO_init(
void *CANdriverState,
uint8_t nodeId,
uint16_t bitRate);
#endif /* CO_NO_LSS_SERVER == 0 */
/* Helper function for NMT master *********************************************/
#if CO_NO_NMT_MASTER == 1
CO_CANtx_t *NMTM_txBuff = 0;
CO_ReturnError_t CO_sendNMTcommand(CO_t *co, uint8_t command, uint8_t nodeID){
if(NMTM_txBuff == 0){
/* error, CO_CANtxBufferInit() was not called for this buffer. */
return CO_ERROR_TX_UNCONFIGURED; /* -11 */
}
NMTM_txBuff->data[0] = command;
NMTM_txBuff->data[1] = nodeID;
CO_ReturnError_t error = CO_ERROR_NO;
/* Apply NMT command also to this node, if set so. */
if(nodeID == 0 || nodeID == co->NMT->nodeId){
switch(command){
case CO_NMT_ENTER_OPERATIONAL:
if((*co->NMT->emPr->errorRegister) == 0) {
co->NMT->operatingState = CO_NMT_OPERATIONAL;
}
break;
case CO_NMT_ENTER_STOPPED:
co->NMT->operatingState = CO_NMT_STOPPED;
break;
case CO_NMT_ENTER_PRE_OPERATIONAL:
co->NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
break;
case CO_NMT_RESET_NODE:
co->NMT->resetCommand = CO_RESET_APP;
break;
case CO_NMT_RESET_COMMUNICATION:
co->NMT->resetCommand = CO_RESET_COMM;
break;
default:
error = CO_ERROR_ILLEGAL_ARGUMENT;
break;
}
}
if(error == CO_ERROR_NO)
return CO_CANsend(co->CANmodule[0], NMTM_txBuff); /* 0 = success */
else
{
return error;
}
}
#endif
#if CO_NO_TRACE > 0
static uint32_t CO_traceBufferSize[CO_NO_TRACE];
#endif
/******************************************************************************/
CO_ReturnError_t CO_new(void)
{
int16_t i;
#ifndef CO_USE_GLOBALS
uint16_t errCnt;
#endif
/* Verify parameters from CO_OD */
if( sizeof(OD_TPDOCommunicationParameter_t) != sizeof(CO_TPDOCommPar_t)
|| sizeof(OD_TPDOMappingParameter_t) != sizeof(CO_TPDOMapPar_t)
|| sizeof(OD_RPDOCommunicationParameter_t) != sizeof(CO_RPDOCommPar_t)
|| sizeof(OD_RPDOMappingParameter_t) != sizeof(CO_RPDOMapPar_t))
{
return CO_ERROR_PARAMETERS;
}
#if CO_NO_SDO_CLIENT != 0
if(sizeof(OD_SDOClientParameter_t) != sizeof(CO_SDOclientPar_t)){
return CO_ERROR_PARAMETERS;
}
#endif
/* Initialize CANopen object */
#ifdef CO_USE_GLOBALS
CO = &COO;
CO_memset((uint8_t*)CO, 0, sizeof(CO_t));
CO->CANmodule[0] = &COO_CANmodule;
CO_CANmodule_rxArray0 = &COO_CANmodule_rxArray0[0];
CO_CANmodule_txArray0 = &COO_CANmodule_txArray0[0];
for(i=0; i<CO_NO_SDO_SERVER; i++)
CO->SDO[i] = &COO_SDO[i];
CO_SDO_ODExtensions = &COO_SDO_ODExtensions[0];
CO->em = &COO_EM;
CO->emPr = &COO_EMpr;
CO->NMT = &COO_NMT;
#if CO_NO_SYNC == 1
CO->SYNC = &COO_SYNC;
#endif
#if CO_NO_TIME == 1
CO->TIME = &COO_TIME;
#endif
for(i=0; i<CO_NO_RPDO; i++)
CO->RPDO[i] = &COO_RPDO[i];
for(i=0; i<CO_NO_TPDO; i++)
CO->TPDO[i] = &COO_TPDO[i];
CO->HBcons = &COO_HBcons;
CO_HBcons_monitoredNodes = &COO_HBcons_monitoredNodes[0];
#if CO_NO_LSS_SERVER == 1
CO->LSSslave = &CO0_LSSslave;
#endif
#if CO_NO_LSS_CLIENT == 1
CO->LSSmaster = &CO0_LSSmaster;
#endif
#if CO_NO_SDO_CLIENT != 0
for(i=0; i<CO_NO_SDO_CLIENT; i++) {
CO->SDOclient[i] = &COO_SDOclient[i];
}
#endif
#if CO_NO_TRACE > 0
for(i=0; i<CO_NO_TRACE; i++) {
CO->trace[i] = &COO_trace[i];
CO_traceTimeBuffers[i] = &COO_traceTimeBuffers[i][0];
CO_traceValueBuffers[i] = &COO_traceValueBuffers[i][0];
CO_traceBufferSize[i] = CO_TRACE_BUFFER_SIZE_FIXED;
}
#endif
#else
if(CO == NULL){ /* Use malloc only once */
CO = &COO;
CO->CANmodule[0] = (CO_CANmodule_t *) calloc(1, sizeof(CO_CANmodule_t));
CO_CANmodule_rxArray0 = (CO_CANrx_t *) calloc(CO_RXCAN_NO_MSGS, sizeof(CO_CANrx_t));
CO_CANmodule_txArray0 = (CO_CANtx_t *) calloc(CO_TXCAN_NO_MSGS, sizeof(CO_CANtx_t));
for(i=0; i<CO_NO_SDO_SERVER; i++){
CO->SDO[i] = (CO_SDO_t *) calloc(1, sizeof(CO_SDO_t));
}
CO_SDO_ODExtensions = (CO_OD_extension_t*) calloc(CO_OD_NoOfElements, sizeof(CO_OD_extension_t));
CO->em = (CO_EM_t *) calloc(1, sizeof(CO_EM_t));
CO->emPr = (CO_EMpr_t *) calloc(1, sizeof(CO_EMpr_t));
CO->NMT = (CO_NMT_t *) calloc(1, sizeof(CO_NMT_t));
#if CO_NO_SYNC == 1
CO->SYNC = (CO_SYNC_t *) calloc(1, sizeof(CO_SYNC_t));
#endif
#if CO_NO_TIME == 1
CO->TIME = (CO_TIME_t *) calloc(1, sizeof(CO_TIME_t));
#endif
for(i=0; i<CO_NO_RPDO; i++){
CO->RPDO[i] = (CO_RPDO_t *) calloc(1, sizeof(CO_RPDO_t));
}
for(i=0; i<CO_NO_TPDO; i++){
CO->TPDO[i] = (CO_TPDO_t *) calloc(1, sizeof(CO_TPDO_t));
}
CO->HBcons = (CO_HBconsumer_t *) calloc(1, sizeof(CO_HBconsumer_t));
CO_HBcons_monitoredNodes = (CO_HBconsNode_t *) calloc(CO_NO_HB_CONS, sizeof(CO_HBconsNode_t));
#if CO_NO_LSS_SERVER == 1
CO->LSSslave = (CO_LSSslave_t *) calloc(1, sizeof(CO_LSSslave_t));
#endif
#if CO_NO_LSS_CLIENT == 1
CO->LSSmaster = (CO_LSSmaster_t *) calloc(1, sizeof(CO_LSSmaster_t));
#endif
#if CO_NO_SDO_CLIENT != 0
for(i=0; i<CO_NO_SDO_CLIENT; i++){
CO->SDOclient[i] = (CO_SDOclient_t *) calloc(1, sizeof(CO_SDOclient_t));
}
#endif
#if CO_NO_TRACE > 0
for(i=0; i<CO_NO_TRACE; i++) {
CO->trace[i] = (CO_trace_t *) calloc(1, sizeof(CO_trace_t));
CO_traceTimeBuffers[i] = (uint32_t *) calloc(OD_traceConfig[i].size, sizeof(uint32_t));
CO_traceValueBuffers[i] = (int32_t *) calloc(OD_traceConfig[i].size, sizeof(int32_t));
if(CO_traceTimeBuffers[i] != NULL && CO_traceValueBuffers[i] != NULL) {
CO_traceBufferSize[i] = OD_traceConfig[i].size;
} else {
CO_traceBufferSize[i] = 0;
}
}
#endif
}
CO_memoryUsed = sizeof(CO_CANmodule_t)
+ sizeof(CO_CANrx_t) * CO_RXCAN_NO_MSGS
+ sizeof(CO_CANtx_t) * CO_TXCAN_NO_MSGS
+ sizeof(CO_SDO_t) * CO_NO_SDO_SERVER
+ sizeof(CO_OD_extension_t) * CO_OD_NoOfElements
+ sizeof(CO_EM_t)
+ sizeof(CO_EMpr_t)
+ sizeof(CO_NMT_t)
#if CO_NO_SYNC == 1
+ sizeof(CO_SYNC_t)
#endif
#if CO_NO_TIME == 1
+ sizeof(CO_TIME_t)
#endif
+ sizeof(CO_RPDO_t) * CO_NO_RPDO
+ sizeof(CO_TPDO_t) * CO_NO_TPDO
+ sizeof(CO_HBconsumer_t)
+ sizeof(CO_HBconsNode_t) * CO_NO_HB_CONS
#if CO_NO_LSS_SERVER == 1
+ sizeof(CO_LSSslave_t)
#endif
#if CO_NO_LSS_CLIENT == 1
+ sizeof(CO_LSSmaster_t)
#endif
#if CO_NO_SDO_CLIENT != 0
+ sizeof(CO_SDOclient_t) * CO_NO_SDO_CLIENT
#endif
+ 0;
#if CO_NO_TRACE > 0
CO_memoryUsed += sizeof(CO_trace_t) * CO_NO_TRACE;
for(i=0; i<CO_NO_TRACE; i++) {
CO_memoryUsed += CO_traceBufferSize[i] * 8;
}
#endif
errCnt = 0;
if(CO->CANmodule[0] == NULL) errCnt++;
if(CO_CANmodule_rxArray0 == NULL) errCnt++;
if(CO_CANmodule_txArray0 == NULL) errCnt++;
for(i=0; i<CO_NO_SDO_SERVER; i++){
if(CO->SDO[i] == NULL) errCnt++;
}
if(CO_SDO_ODExtensions == NULL) errCnt++;
if(CO->em == NULL) errCnt++;
if(CO->emPr == NULL) errCnt++;
if(CO->NMT == NULL) errCnt++;
#if CO_NO_SYNC == 1
if(CO->SYNC == NULL) errCnt++;
#endif
#if CO_NO_TIME == 1
if(CO->TIME == NULL) errCnt++;
#endif
for(i=0; i<CO_NO_RPDO; i++){
if(CO->RPDO[i] == NULL) errCnt++;
}
for(i=0; i<CO_NO_TPDO; i++){
if(CO->TPDO[i] == NULL) errCnt++;
}
if(CO->HBcons == NULL) errCnt++;
if(CO_HBcons_monitoredNodes == NULL) errCnt++;
#if CO_NO_LSS_SERVER == 1
if(CO->LSSslave == NULL) errCnt++;
#endif
#if CO_NO_LSS_CLIENT == 1
if(CO->LSSmaster == NULL) errCnt++;
#endif
#if CO_NO_SDO_CLIENT != 0
for(i=0; i<CO_NO_SDO_CLIENT; i++){
if(CO->SDOclient[i] == NULL) errCnt++;
}
#endif
#if CO_NO_TRACE > 0
for(i=0; i<CO_NO_TRACE; i++) {
if(CO->trace[i] == NULL) errCnt++;
}
#endif
if(errCnt != 0) return CO_ERROR_OUT_OF_MEMORY;
#endif
return CO_ERROR_NO;
}
/******************************************************************************/
CO_ReturnError_t CO_CANinit(
void *CANdriverState,
uint16_t bitRate)
{
CO_ReturnError_t err;
CO->CANmodule[0]->CANnormal = false;
CO_CANsetConfigurationMode(CANdriverState);
err = CO_CANmodule_init(
CO->CANmodule[0],
CANdriverState,
CO_CANmodule_rxArray0,
CO_RXCAN_NO_MSGS,
CO_CANmodule_txArray0,
CO_TXCAN_NO_MSGS,
bitRate);
return err;
}
/******************************************************************************/
#if CO_NO_LSS_SERVER == 1
CO_ReturnError_t CO_LSSinit(
uint8_t nodeId,
uint16_t bitRate)
{
CO_LSS_address_t lssAddress;
CO_ReturnError_t err;
lssAddress.identity.productCode = OD_identity.productCode;
lssAddress.identity.revisionNumber = OD_identity.revisionNumber;
lssAddress.identity.serialNumber = OD_identity.serialNumber;
lssAddress.identity.vendorID = OD_identity.vendorID;
err = CO_LSSslave_init(
CO->LSSslave,
lssAddress,
bitRate,
nodeId,
CO->CANmodule[0],
CO_RXCAN_LSS,
CO_CAN_ID_LSS_SRV,
CO->CANmodule[0],
CO_TXCAN_LSS,
CO_CAN_ID_LSS_CLI);
return err;
}
#endif /* CO_NO_LSS_SERVER == 1 */
/******************************************************************************/
CO_ReturnError_t CO_CANopenInit(
uint8_t nodeId)
{
int16_t i;
CO_ReturnError_t err;
/* Verify CANopen Node-ID */
if(nodeId<1 || nodeId>127) {
return CO_ERROR_PARAMETERS;
}
for (i=0; i<CO_NO_SDO_SERVER; i++)
{
uint32_t COB_IDClientToServer;
uint32_t COB_IDServerToClient;
if(i==0){
/*Default SDO server must be located at first index*/
COB_IDClientToServer = CO_CAN_ID_RSDO + nodeId;
COB_IDServerToClient = CO_CAN_ID_TSDO + nodeId;
}else{
COB_IDClientToServer = OD_SDOServerParameter[i].COB_IDClientToServer;
COB_IDServerToClient = OD_SDOServerParameter[i].COB_IDServerToClient;
}
err = CO_SDO_init(
CO->SDO[i],
COB_IDClientToServer,
COB_IDServerToClient,
OD_H1200_SDO_SERVER_PARAM+i,
i==0 ? 0 : CO->SDO[0],
&CO_OD[0],
CO_OD_NoOfElements,
CO_SDO_ODExtensions,
nodeId,
CO->CANmodule[0],
CO_RXCAN_SDO_SRV+i,
CO->CANmodule[0],
CO_TXCAN_SDO_SRV+i);
}
if(err){return err;}
err = CO_EM_init(
CO->em,
CO->emPr,
CO->SDO[0],
&OD_errorStatusBits[0],
ODL_errorStatusBits_stringLength,
&OD_errorRegister,
&OD_preDefinedErrorField[0],
ODL_preDefinedErrorField_arrayLength,
CO->CANmodule[0],
CO_RXCAN_EMERG,
CO->CANmodule[0],
CO_TXCAN_EMERG,
(uint16_t)CO_CAN_ID_EMERGENCY + nodeId);
if(err){return err;}
err = CO_NMT_init(
CO->NMT,
CO->emPr,
nodeId,
500,
CO->CANmodule[0],
CO_RXCAN_NMT,
CO_CAN_ID_NMT_SERVICE,
CO->CANmodule[0],
CO_TXCAN_HB,
CO_CAN_ID_HEARTBEAT + nodeId);
if(err){return err;}
#if CO_NO_NMT_MASTER == 1
NMTM_txBuff = CO_CANtxBufferInit(/* return pointer to 8-byte CAN data buffer, which should be populated */
CO->CANmodule[0], /* pointer to CAN module used for sending this message */
CO_TXCAN_NMT, /* index of specific buffer inside CAN module */
0x0000, /* CAN identifier */
0, /* rtr */
2, /* number of data bytes */
0); /* synchronous message flag bit */
#endif
#if CO_NO_LSS_CLIENT == 1
err = CO_LSSmaster_init(
CO->LSSmaster,
CO_LSSmaster_DEFAULT_TIMEOUT,
CO->CANmodule[0],
CO_RXCAN_LSS,
CO_CAN_ID_LSS_CLI,
CO->CANmodule[0],
CO_TXCAN_LSS,
CO_CAN_ID_LSS_SRV);
if(err){return err;}
#endif
#if CO_NO_SYNC == 1
err = CO_SYNC_init(
CO->SYNC,
CO->em,
CO->SDO[0],
&CO->NMT->operatingState,
OD_COB_ID_SYNCMessage,
OD_communicationCyclePeriod,
OD_synchronousCounterOverflowValue,
CO->CANmodule[0],
CO_RXCAN_SYNC,
CO->CANmodule[0],
CO_TXCAN_SYNC);
if(err){return err;}
#endif
#if CO_NO_TIME == 1
err = CO_TIME_init(
CO->TIME,
CO->em,
CO->SDO[0],
&CO->NMT->operatingState,
OD_COB_ID_TIME,
0,
CO->CANmodule[0],
CO_RXCAN_TIME,
CO->CANmodule[0],
CO_TXCAN_TIME);
if(err){return err;}
#endif
for(i=0; i<CO_NO_RPDO; i++){
CO_CANmodule_t *CANdevRx = CO->CANmodule[0];
uint16_t CANdevRxIdx = CO_RXCAN_RPDO + i;
err = CO_RPDO_init(
CO->RPDO[i],
CO->em,
CO->SDO[0],
CO->SYNC,
&CO->NMT->operatingState,
nodeId,
((i<4) ? (CO_CAN_ID_RPDO_1+i*0x100) : 0),
0,
(CO_RPDOCommPar_t*) &OD_RPDOCommunicationParameter[i],
(CO_RPDOMapPar_t*) &OD_RPDOMappingParameter[i],
OD_H1400_RXPDO_1_PARAM+i,
OD_H1600_RXPDO_1_MAPPING+i,
CANdevRx,
CANdevRxIdx);
if(err){return err;}
}
for(i=0; i<CO_NO_TPDO; i++){
err = CO_TPDO_init(
CO->TPDO[i],
CO->em,
CO->SDO[0],
CO->SYNC,
&CO->NMT->operatingState,
nodeId,
((i<4) ? (CO_CAN_ID_TPDO_1+i*0x100) : 0),
0,
(CO_TPDOCommPar_t*) &OD_TPDOCommunicationParameter[i],
(CO_TPDOMapPar_t*) &OD_TPDOMappingParameter[i],
OD_H1800_TXPDO_1_PARAM+i,
OD_H1A00_TXPDO_1_MAPPING+i,
CO->CANmodule[0],
CO_TXCAN_TPDO+i);
if(err){return err;}
}
err = CO_HBconsumer_init(
CO->HBcons,
CO->em,
CO->SDO[0],
&OD_consumerHeartbeatTime[0],
CO_HBcons_monitoredNodes,
CO_NO_HB_CONS,
CO->CANmodule[0],
CO_RXCAN_CONS_HB);
if(err){return err;}
#if CO_NO_SDO_CLIENT != 0
for(i=0; i<CO_NO_SDO_CLIENT; i++){
err = CO_SDOclient_init(
CO->SDOclient[i],
CO->SDO[0],
(CO_SDOclientPar_t*) &OD_SDOClientParameter[i],
CO->CANmodule[0],
CO_RXCAN_SDO_CLI+i,
CO->CANmodule[0],
CO_TXCAN_SDO_CLI+i);
if(err){return err;}
}
#endif
#if CO_NO_TRACE > 0
for(i=0; i<CO_NO_TRACE; i++) {
CO_trace_init(
CO->trace[i],
CO->SDO[0],
OD_traceConfig[i].axisNo,
CO_traceTimeBuffers[i],
CO_traceValueBuffers[i],
CO_traceBufferSize[i],
&OD_traceConfig[i].map,
&OD_traceConfig[i].format,
&OD_traceConfig[i].trigger,
&OD_traceConfig[i].threshold,
&OD_trace[i].value,
&OD_trace[i].min,
&OD_trace[i].max,
&OD_trace[i].triggerTime,
OD_INDEX_TRACE_CONFIG + i,
OD_INDEX_TRACE + i);
}
#endif
return CO_ERROR_NO;
}
/******************************************************************************/
CO_ReturnError_t CO_init(
void *CANdriverState,
uint8_t nodeId,
uint16_t bitRate)
{
CO_ReturnError_t err;
err = CO_new();
if (err) {
return err;
}
err = CO_CANinit(CANdriverState, bitRate);
if (err) {
CO_delete(CANdriverState);
return err;
}
err = CO_CANopenInit(nodeId);
if (err) {
CO_delete(CANdriverState);
return err;
}
return CO_ERROR_NO;
}
/******************************************************************************/
void CO_delete(void *CANdriverState){
#ifndef CO_USE_GLOBALS
int16_t i;
#endif
CO_CANsetConfigurationMode(CANdriverState);
CO_CANmodule_disable(CO->CANmodule[0]);
#ifndef CO_USE_GLOBALS
#if CO_NO_TRACE > 0
for(i=0; i<CO_NO_TRACE; i++) {
free(CO->trace[i]);
free(CO_traceTimeBuffers[i]);
free(CO_traceValueBuffers[i]);
}
#endif
#if CO_NO_SDO_CLIENT != 0
for(i=0; i<CO_NO_SDO_CLIENT; i++) {
free(CO->SDOclient[i]);
}
#endif
#if CO_NO_LSS_SERVER == 1
free(CO->LSSslave);
#endif
#if CO_NO_LSS_CLIENT == 1
free(CO->LSSmaster);
#endif
free(CO_HBcons_monitoredNodes);
free(CO->HBcons);
for(i=0; i<CO_NO_RPDO; i++){
free(CO->RPDO[i]);
}
for(i=0; i<CO_NO_TPDO; i++){
free(CO->TPDO[i]);
}
#if CO_NO_SYNC == 1
free(CO->SYNC);
#endif
#if CO_NO_TIME == 1
free(CO->TIME);
#endif
free(CO->NMT);
free(CO->emPr);
free(CO->em);
free(CO_SDO_ODExtensions);
for(i=0; i<CO_NO_SDO_SERVER; i++){
free(CO->SDO[i]);
}
free(CO_CANmodule_txArray0);
free(CO_CANmodule_rxArray0);
free(CO->CANmodule[0]);
CO = NULL;
#endif
}
/******************************************************************************/
CO_NMT_reset_cmd_t CO_process(
CO_t *co,
uint16_t timeDifference_ms,
uint16_t *timerNext_ms)
{
uint8_t i;
bool_t NMTisPreOrOperational = false;
CO_NMT_reset_cmd_t reset = CO_RESET_NOT;
#ifdef CO_USE_LEDS
static uint16_t ms50 = 0;
#endif /* CO_USE_LEDS */
if(co->NMT->operatingState == CO_NMT_PRE_OPERATIONAL || co->NMT->operatingState == CO_NMT_OPERATIONAL)
NMTisPreOrOperational = true;
#ifdef CO_USE_LEDS
ms50 += timeDifference_ms;
if(ms50 >= 50){
ms50 -= 50;
CO_NMT_blinkingProcess50ms(co->NMT);
}
#endif /* CO_USE_LEDS */
for(i=0; i<CO_NO_SDO_SERVER; i++){
CO_SDO_process(
co->SDO[i],
NMTisPreOrOperational,
timeDifference_ms,
1000,
timerNext_ms);
}
CO_EM_process(
co->emPr,
NMTisPreOrOperational,
timeDifference_ms * 10,
OD_inhibitTimeEMCY,
timerNext_ms);
reset = CO_NMT_process(
co->NMT,
timeDifference_ms,
OD_producerHeartbeatTime,
OD_NMTStartup,
OD_errorRegister,
OD_errorBehavior,
timerNext_ms);
CO_HBconsumer_process(
co->HBcons,
NMTisPreOrOperational,
timeDifference_ms);
#if CO_NO_TIME == 1
CO_TIME_process(
co->TIME,
timeDifference_ms);
#endif
return reset;
}
/******************************************************************************/
#if CO_NO_SYNC == 1
bool_t CO_process_SYNC(
CO_t *co,
uint32_t timeDifference_us)
{
bool_t syncWas = false;
switch(CO_SYNC_process(co->SYNC, timeDifference_us, OD_synchronousWindowLength)){
case 1: //immediately after the SYNC message
syncWas = true;
break;
case 2: //outside SYNC window
CO_CANclearPendingSyncPDOs(co->CANmodule[0]);
break;
}
return syncWas;
}
#endif
/******************************************************************************/
void CO_process_RPDO(
CO_t *co,
bool_t syncWas)
{
int16_t i;
for(i=0; i<CO_NO_RPDO; i++){
CO_RPDO_process(co->RPDO[i], syncWas);
}
}
/******************************************************************************/
void CO_process_TPDO(
CO_t *co,
bool_t syncWas,
uint32_t timeDifference_us)
{
int16_t i;
/* Verify PDO Change Of State and process PDOs */
for(i=0; i<CO_NO_TPDO; i++){
if(!co->TPDO[i]->sendRequest)
co->TPDO[i]->sendRequest = CO_TPDOisCOS(co->TPDO[i]);
CO_TPDO_process(co->TPDO[i], syncWas, timeDifference_us);
}
}

View File

@ -0,0 +1,324 @@
/**
* Main CANopen stack file.
*
* It combines Object dictionary (CO_OD) and all other CANopen source files.
* Configuration information are read from CO_OD.h file. This file uses one
* CAN module. If multiple CAN modules are to be used, then this file may be
* customized for different CANopen configuration. (One or multiple CANopen
* device on one or multiple CAN modules.)
*
* @file CANopen.h
* @ingroup CO_CANopen
* @author Janez Paternoster
* @author Uwe Kindler
* @copyright 2010 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CANopen_H
#define CANopen_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup CO_CANopen CANopen stack
* @{
*
* CANopenNode is free and open source CANopen Stack.
*
* CANopen is the internationally standardized (EN 50325-4) (CiA DS-301)
* CAN-based higher-layer protocol for embedded control system. For more
* information on CANopen see http://www.can-cia.org/
*
* CANopenNode homepage is https://github.com/CANopenNode/CANopenNode
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "CO_driver.h"
#include "CO_OD.h"
#include "CO_SDO.h"
#include "CO_Emergency.h"
#include "CO_NMT_Heartbeat.h"
#include "CO_SYNC.h"
#include "CO_TIME.h"
#include "CO_PDO.h"
#include "CO_HBconsumer.h"
#if CO_NO_SDO_CLIENT != 0
#include "CO_SDOmaster.h"
#endif
#if CO_NO_TRACE > 0
#include "CO_trace.h"
#endif
#if CO_NO_LSS_SERVER == 1
#include "CO_LSSslave.h"
#endif
#if CO_NO_LSS_CLIENT == 1
#include "CO_LSSmaster.h"
#endif
/**
* Default CANopen identifiers.
*
* Default CANopen identifiers for CANopen communication objects. Same as
* 11-bit addresses of CAN messages. These are default identifiers and
* can be changed in CANopen. Especially PDO identifiers are confgured
* in PDO linking phase of the CANopen network configuration.
*/
typedef enum{
CO_CAN_ID_NMT_SERVICE = 0x000, /**< 0x000, Network management */
CO_CAN_ID_SYNC = 0x080, /**< 0x080, Synchronous message */
CO_CAN_ID_EMERGENCY = 0x080, /**< 0x080, Emergency messages (+nodeID) */
CO_CAN_ID_TIME = 0x100, /**< 0x100, Time message */
CO_CAN_ID_TPDO_1 = 0x180, /**< 0x180, Default TPDO1 (+nodeID) */
CO_CAN_ID_RPDO_1 = 0x200, /**< 0x200, Default RPDO1 (+nodeID) */
CO_CAN_ID_TPDO_2 = 0x280, /**< 0x280, Default TPDO2 (+nodeID) */
CO_CAN_ID_RPDO_2 = 0x300, /**< 0x300, Default RPDO2 (+nodeID) */
CO_CAN_ID_TPDO_3 = 0x380, /**< 0x380, Default TPDO3 (+nodeID) */
CO_CAN_ID_RPDO_3 = 0x400, /**< 0x400, Default RPDO3 (+nodeID) */
CO_CAN_ID_TPDO_4 = 0x480, /**< 0x480, Default TPDO4 (+nodeID) */
CO_CAN_ID_RPDO_4 = 0x500, /**< 0x500, Default RPDO5 (+nodeID) */
CO_CAN_ID_TSDO = 0x580, /**< 0x580, SDO response from server (+nodeID) */
CO_CAN_ID_RSDO = 0x600, /**< 0x600, SDO request from client (+nodeID) */
CO_CAN_ID_HEARTBEAT = 0x700, /**< 0x700, Heartbeat message */
CO_CAN_ID_LSS_CLI = 0x7E4, /**< 0x7E4, LSS response from server to client */
CO_CAN_ID_LSS_SRV = 0x7E5 /**< 0x7E5, LSS request from client to server */
}CO_Default_CAN_ID_t;
/**
* CANopen stack object combines pointers to all CANopen objects.
*/
typedef struct{
CO_CANmodule_t *CANmodule[1]; /**< CAN module objects */
CO_SDO_t *SDO[CO_NO_SDO_SERVER]; /**< SDO object */
CO_EM_t *em; /**< Emergency report object */
CO_EMpr_t *emPr; /**< Emergency process object */
CO_NMT_t *NMT; /**< NMT object */
CO_SYNC_t *SYNC; /**< SYNC object */
CO_TIME_t *TIME; /**< TIME object */
CO_RPDO_t *RPDO[CO_NO_RPDO];/**< RPDO objects */
CO_TPDO_t *TPDO[CO_NO_TPDO];/**< TPDO objects */
CO_HBconsumer_t *HBcons; /**< Heartbeat consumer object*/
#if CO_NO_LSS_SERVER == 1
CO_LSSslave_t *LSSslave; /**< LSS server/slave object */
#endif
#if CO_NO_LSS_CLIENT == 1
CO_LSSmaster_t *LSSmaster; /**< LSS master/client object */
#endif
#if CO_NO_SDO_CLIENT != 0
CO_SDOclient_t *SDOclient[CO_NO_SDO_CLIENT]; /**< SDO client object */
#endif
#if CO_NO_TRACE > 0
CO_trace_t *trace[CO_NO_TRACE]; /**< Trace object for monitoring variables */
#endif
}CO_t;
/** CANopen object */
extern CO_t *CO;
/**
* Function CO_sendNMTcommand() is simple function, which sends CANopen message.
* This part of code is an example of custom definition of simple CANopen
* object. Follow the code in CANopen.c file. If macro CO_NO_NMT_MASTER is 1,
* function CO_sendNMTcommand can be used to send NMT master message.
*
* @param co CANopen object.
* @param command NMT command.
* @param nodeID Node ID.
*
* @return 0: Operation completed successfully.
* @return other: same as CO_CANsend().
*/
#if CO_NO_NMT_MASTER == 1
CO_ReturnError_t CO_sendNMTcommand(CO_t *co, uint8_t command, uint8_t nodeID);
#endif
#if CO_NO_LSS_SERVER == 1
/**
* Allocate and initialize memory for CANopen object
*
* Function must be called in the communication reset section.
*
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT,
* CO_ERROR_OUT_OF_MEMORY
*/
CO_ReturnError_t CO_new(void);
/**
* Initialize CAN driver
*
* Function must be called in the communication reset section.
*
* @param CANdriverState Pointer to the CAN module, passed to CO_CANmodule_init().
* @param bitRate CAN bit rate.
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT,
* CO_ERROR_ILLEGAL_BAUDRATE, CO_ERROR_OUT_OF_MEMORY
*/
CO_ReturnError_t CO_CANinit(
void *CANdriverState,
uint16_t bitRate);
/**
* Initialize CANopen LSS slave
*
* Function must be called in the communication reset section.
*
* @param nodeId Node ID of the CANopen device (1 ... 127) or CO_LSS_NODE_ID_ASSIGNMENT
* @param bitRate CAN bit rate.
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT
*/
CO_ReturnError_t CO_LSSinit(
uint8_t nodeId,
uint16_t bitRate);
/**
* Initialize CANopen stack.
*
* Function must be called in the communication reset section.
*
* @param nodeId Node ID of the CANopen device (1 ... 127).
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT
*/
CO_ReturnError_t CO_CANopenInit(
uint8_t nodeId);
#else /* CO_NO_LSS_SERVER == 1 */
/**
* Initialize CANopen stack.
*
* Function must be called in the communication reset section.
*
* @param CANdriverState Pointer to the user-defined CAN base structure, passed to CO_CANmodule_init().
* @param nodeId Node ID of the CANopen device (1 ... 127).
* @param bitRate CAN bit rate.
*
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT,
* CO_ERROR_OUT_OF_MEMORY, CO_ERROR_ILLEGAL_BAUDRATE
*/
CO_ReturnError_t CO_init(
void *CANdriverState,
uint8_t nodeId,
uint16_t bitRate);
#endif /* CO_NO_LSS_SERVER == 1 */
/**
* Delete CANopen object and free memory. Must be called at program exit.
*
* @param CANdriverState Pointer to the user-defined CAN base structure, passed to CO_CANmodule_init().
*/
void CO_delete(void *CANdriverState);
/**
* Process CANopen objects.
*
* Function must be called cyclically. It processes all "asynchronous" CANopen
* objects.
*
* @param co CANopen object.
* @param timeDifference_ms Time difference from previous function call in [milliseconds].
* @param timerNext_ms Return value - info to OS - maximum delay after function
* should be called next time in [milliseconds]. Value can be used for OS
* sleep time. Initial value must be set to something, 50ms typically.
* Output will be equal or lower to initial value. If there is new object
* to process, delay should be suspended and this function should be
* called immediately. Parameter is ignored if NULL.
*
* @return #CO_NMT_reset_cmd_t from CO_NMT_process().
*/
CO_NMT_reset_cmd_t CO_process(
CO_t *co,
uint16_t timeDifference_ms,
uint16_t *timerNext_ms);
#if CO_NO_SYNC == 1
/**
* Process CANopen SYNC objects.
*
* Function must be called cyclically from real time thread with constant
* interval (1ms typically). It processes SYNC CANopen objects.
*
* @param co CANopen object.
* @param timeDifference_us Time difference from previous function call in [microseconds].
*
* @return True, if CANopen SYNC message was just received or transmitted.
*/
bool_t CO_process_SYNC(
CO_t *co,
uint32_t timeDifference_us);
#endif
/**
* Process CANopen RPDO objects.
*
* Function must be called cyclically from real time thread with constant.
* interval (1ms typically). It processes receive PDO CANopen objects.
*
* @param co CANopen object.
* @param syncWas True, if CANopen SYNC message was just received or transmitted.
*/
void CO_process_RPDO(
CO_t *co,
bool_t syncWas);
/**
* Process CANopen TPDO objects.
*
* Function must be called cyclically from real time thread with constant.
* interval (1ms typically). It processes transmit PDO CANopen objects.
*
* @param co CANopen object.
* @param syncWas True, if CANopen SYNC message was just received or transmitted.
* @param timeDifference_us Time difference from previous function call in [microseconds].
*/
void CO_process_TPDO(
CO_t *co,
bool_t syncWas,
uint32_t timeDifference_us);
#ifdef __cplusplus
}
#endif /*__cplusplus*/
/** @} */
#endif

View File

@ -0,0 +1,469 @@
/*
* CANopen Emergency object.
*
* @file CO_Emergency.c
* @ingroup CO_Emergency
* @author Janez Paternoster
* @copyright 2004 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "CO_driver.h"
#include "CO_SDO.h"
#include "CO_Emergency.h"
#include "CANopen.h"
/*
* Read received message from CAN module.
*
* Function will be called (by CAN receive interrupt) every time, when CAN
* message with correct identifier will be received. For more information and
* description of parameters see file CO_driver.h.
*/
static void CO_EM_receive(void *object, const CO_CANrxMsg_t *msg){
CO_EM_t *em;
uint16_t errorCode;
uint32_t infoCode;
em = (CO_EM_t*)object;
if(em!=NULL && em->pFunctSignalRx!=NULL){
CO_memcpySwap2(&errorCode, &msg->data[0]);
CO_memcpySwap4(&infoCode, &msg->data[4]);
em->pFunctSignalRx(CO_CANrxMsg_readIdent(msg),
errorCode,
msg->data[2],
msg->data[3],
infoCode);
}
}
/*
* Function for accessing _Pre-Defined Error Field_ (index 0x1003) from SDO server.
*
* For more information see file CO_SDO.h.
*/
static CO_SDO_abortCode_t CO_ODF_1003(CO_ODF_arg_t *ODF_arg);
static CO_SDO_abortCode_t CO_ODF_1003(CO_ODF_arg_t *ODF_arg){
CO_EMpr_t *emPr;
uint8_t value;
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
emPr = (CO_EMpr_t*) ODF_arg->object;
value = ODF_arg->data[0];
if(ODF_arg->reading){
uint8_t noOfErrors;
noOfErrors = emPr->preDefErrNoOfErrors;
if(ODF_arg->subIndex == 0U){
ODF_arg->data[0] = noOfErrors;
}
else if(ODF_arg->subIndex > noOfErrors){
ret = CO_SDO_AB_NO_DATA;
}
else{
ret = CO_SDO_AB_NONE;
}
}
else{
/* only '0' may be written to subIndex 0 */
if(ODF_arg->subIndex == 0U){
if(value == 0U){
emPr->preDefErrNoOfErrors = 0U;
}
else{
ret = CO_SDO_AB_INVALID_VALUE;
}
}
else{
ret = CO_SDO_AB_READONLY;
}
}
return ret;
}
/*
* Function for accessing _COB ID EMCY_ (index 0x1014) from SDO server.
*
* For more information see file CO_SDO.h.
*/
static CO_SDO_abortCode_t CO_ODF_1014(CO_ODF_arg_t *ODF_arg);
static CO_SDO_abortCode_t CO_ODF_1014(CO_ODF_arg_t *ODF_arg){
uint8_t *nodeId;
uint32_t value;
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
nodeId = (uint8_t*) ODF_arg->object;
value = CO_getUint32(ODF_arg->data);
/* add nodeId to the value */
if(ODF_arg->reading){
CO_setUint32(ODF_arg->data, value + *nodeId);
}
return ret;
}
/******************************************************************************/
CO_ReturnError_t CO_EM_init(
CO_EM_t *em,
CO_EMpr_t *emPr,
CO_SDO_t *SDO,
uint8_t *errorStatusBits,
uint8_t errorStatusBitsSize,
uint8_t *errorRegister,
uint32_t *preDefErr,
uint8_t preDefErrSize,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdx,
CO_CANmodule_t *CANdevTx,
uint16_t CANdevTxIdx,
uint16_t CANidTxEM)
{
uint8_t i;
/* verify arguments */
if(em==NULL || emPr==NULL || SDO==NULL || errorStatusBits==NULL || errorStatusBitsSize<6U ||
errorRegister==NULL || preDefErr==NULL || CANdevTx==NULL || CANdevRx==NULL){
return CO_ERROR_ILLEGAL_ARGUMENT;
}
/* Configure object variables */
em->errorStatusBits = errorStatusBits;
em->errorStatusBitsSize = errorStatusBitsSize;
em->bufEnd = em->buf + (CO_EM_INTERNAL_BUFFER_SIZE * 8);
em->bufWritePtr = em->buf;
em->bufReadPtr = em->buf;
em->bufFull = 0U;
em->wrongErrorReport = 0U;
em->pFunctSignal = NULL;
em->pFunctSignalRx = NULL;
emPr->em = em;
emPr->errorRegister = errorRegister;
emPr->preDefErr = preDefErr;
emPr->preDefErrSize = preDefErrSize;
emPr->preDefErrNoOfErrors = 0U;
emPr->inhibitEmTimer = 0U;
/* clear error status bits */
for(i=0U; i<errorStatusBitsSize; i++){
em->errorStatusBits[i] = 0U;
}
/* Configure Object dictionary entry at index 0x1003 and 0x1014 */
CO_OD_configure(SDO, OD_H1003_PREDEF_ERR_FIELD, CO_ODF_1003, (void*)emPr, 0, 0U);
CO_OD_configure(SDO, OD_H1014_COBID_EMERGENCY, CO_ODF_1014, (void*)&SDO->nodeId, 0, 0U);
/* configure SDO server CAN reception */
CO_CANrxBufferInit(
CANdevRx, /* CAN device */
CANdevRxIdx, /* rx buffer index */
CO_CAN_ID_EMERGENCY, /* CAN identifier */
0x780, /* mask */
0, /* rtr */
(void*)em, /* object passed to receive function */
CO_EM_receive); /* this function will process received message */
/* configure emergency message CAN transmission */
emPr->CANdev = CANdevTx;
emPr->CANdev->em = (void*)em; /* update pointer inside CAN device. */
emPr->CANtxBuff = CO_CANtxBufferInit(
CANdevTx, /* CAN device */
CANdevTxIdx, /* index of specific buffer inside CAN module */
CANidTxEM, /* CAN identifier */
0, /* rtr */
8U, /* number of data bytes */
0); /* synchronous message flag bit */
return CO_ERROR_NO;
}
/******************************************************************************/
void CO_EM_initCallback(
CO_EM_t *em,
void (*pFunctSignal)(void))
{
if(em != NULL){
em->pFunctSignal = pFunctSignal;
}
}
/******************************************************************************/
void CO_EM_initCallbackRx(
CO_EM_t *em,
void (*pFunctSignalRx)(const uint16_t ident,
const uint16_t errorCode,
const uint8_t errorRegister,
const uint8_t errorBit,
const uint32_t infoCode))
{
if(em != NULL){
em->pFunctSignalRx = pFunctSignalRx;
}
}
/******************************************************************************/
void CO_EM_process(
CO_EMpr_t *emPr,
bool_t NMTisPreOrOperational,
uint16_t timeDifference_100us,
uint16_t emInhTime,
uint16_t *timerNext_ms)
{
CO_EM_t *em = emPr->em;
uint8_t errorRegister;
uint8_t errorMask;
uint8_t i;
/* verify errors from driver and other */
CO_CANverifyErrors(emPr->CANdev);
if(em->wrongErrorReport != 0U){
CO_errorReport(em, CO_EM_WRONG_ERROR_REPORT, CO_EMC_SOFTWARE_INTERNAL, (uint32_t)em->wrongErrorReport);
em->wrongErrorReport = 0U;
}
/* calculate Error register */
errorRegister = 0U;
errorMask = (uint8_t)~(CO_ERR_REG_GENERIC_ERR | CO_ERR_REG_COMM_ERR | CO_ERR_REG_MANUFACTURER);
/* generic error */
if(em->errorStatusBits[5]){
errorRegister |= CO_ERR_REG_GENERIC_ERR;
}
/* communication error (overrun, error state) */
if(em->errorStatusBits[2] || em->errorStatusBits[3]){
errorRegister |= CO_ERR_REG_COMM_ERR;
}
/* Manufacturer */
for(i=6; i<em->errorStatusBitsSize; i++) {
if (em->errorStatusBits[i]) {
errorRegister |= CO_ERR_REG_MANUFACTURER;
}
}
*emPr->errorRegister = (*emPr->errorRegister & errorMask) | errorRegister;
/* inhibit time */
if(emPr->inhibitEmTimer < emInhTime){
emPr->inhibitEmTimer += timeDifference_100us;
}
/* send Emergency message. */
if( NMTisPreOrOperational &&
!emPr->CANtxBuff->bufferFull &&
(em->bufReadPtr != em->bufWritePtr || em->bufFull))
{
uint32_t preDEF; /* preDefinedErrorField */
uint16_t diff;
if (emPr->inhibitEmTimer >= emInhTime) {
/* inhibit time elapsed, send message */
/* add error register */
em->bufReadPtr[2] = *emPr->errorRegister;
/* copy data to CAN emergency message */
CO_memcpy(emPr->CANtxBuff->data, em->bufReadPtr, 8U);
CO_memcpy((uint8_t*)&preDEF, em->bufReadPtr, 4U);
em->bufReadPtr += 8;
/* Update read buffer pointer and reset inhibit timer */
if(em->bufReadPtr == em->bufEnd){
em->bufReadPtr = em->buf;
}
emPr->inhibitEmTimer = 0U;
/* verify message buffer overflow, then clear full flag */
if(em->bufFull == 2U){
em->bufFull = 0U; /* will be updated below */
CO_errorReport(em, CO_EM_EMERGENCY_BUFFER_FULL, CO_EMC_GENERIC, 0U);
}
else{
em->bufFull = 0;
CO_errorReset(em, CO_EM_EMERGENCY_BUFFER_FULL, 0);
}
/* write to 'pre-defined error field' (object dictionary, index 0x1003) */
if(emPr->preDefErr){
uint8_t j;
if(emPr->preDefErrNoOfErrors < emPr->preDefErrSize)
emPr->preDefErrNoOfErrors++;
for(j=emPr->preDefErrNoOfErrors-1; j>0; j--)
emPr->preDefErr[j] = emPr->preDefErr[j-1];
emPr->preDefErr[0] = preDEF;
}
/* send CAN message */
CO_CANsend(emPr->CANdev, emPr->CANtxBuff);
}
/* check again after inhibit time elapsed */
diff = (emInhTime + 9) / 10; /* time difference in ms, always round up */
if (timerNext_ms != NULL && *timerNext_ms > diff) {
*timerNext_ms = diff;
}
}
return;
}
/******************************************************************************/
void CO_errorReport(CO_EM_t *em, const uint8_t errorBit, const uint16_t errorCode, const uint32_t infoCode){
uint8_t index = errorBit >> 3;
uint8_t bitmask = 1 << (errorBit & 0x7);
uint8_t *errorStatusBits = 0;
bool_t sendEmergency = true;
if(em == NULL){
sendEmergency = false;
}
else if(index >= em->errorStatusBitsSize){
/* if errorBit value not supported, send emergency 'CO_EM_WRONG_ERROR_REPORT' */
em->wrongErrorReport = errorBit;
sendEmergency = false;
}
else{
errorStatusBits = &em->errorStatusBits[index];
/* if error was already reported, do nothing */
if((*errorStatusBits & bitmask) != 0){
sendEmergency = false;
}
}
if(sendEmergency){
/* set error bit */
if(errorBit){
/* any error except NO_ERROR */
*errorStatusBits |= bitmask;
}
/* verify buffer full, set overflow */
if(em->bufFull){
em->bufFull = 2;
}
else{
uint8_t bufCopy[8];
/* prepare data for emergency message */
CO_memcpySwap2(&bufCopy[0], &errorCode);
bufCopy[2] = 0; /* error register will be set later */
bufCopy[3] = errorBit;
CO_memcpySwap4(&bufCopy[4], &infoCode);
/* copy data to the buffer, increment writePtr and verify buffer full */
CO_LOCK_EMCY();
CO_memcpy(em->bufWritePtr, &bufCopy[0], 8);
em->bufWritePtr += 8;
if(em->bufWritePtr == em->bufEnd) em->bufWritePtr = em->buf;
if(em->bufWritePtr == em->bufReadPtr) em->bufFull = 1;
CO_UNLOCK_EMCY();
/* Optional signal to RTOS, which can resume task, which handles CO_EM_process */
if(em->pFunctSignal != NULL) {
em->pFunctSignal();
}
}
}
}
/******************************************************************************/
void CO_errorReset(CO_EM_t *em, const uint8_t errorBit, const uint32_t infoCode){
uint8_t index = errorBit >> 3;
uint8_t bitmask = 1 << (errorBit & 0x7);
uint8_t *errorStatusBits = 0;
bool_t sendEmergency = true;
if(em == NULL){
sendEmergency = false;
}
else if(index >= em->errorStatusBitsSize){
/* if errorBit value not supported, send emergency 'CO_EM_WRONG_ERROR_REPORT' */
em->wrongErrorReport = errorBit;
sendEmergency = false;
}
else{
errorStatusBits = &em->errorStatusBits[index];
/* if error was allready cleared, do nothing */
if((*errorStatusBits & bitmask) == 0){
sendEmergency = false;
}
}
if(sendEmergency){
/* erase error bit */
*errorStatusBits &= ~bitmask;
/* verify buffer full */
if(em->bufFull){
em->bufFull = 2;
}
else{
uint8_t bufCopy[8];
/* prepare data for emergency message */
bufCopy[0] = 0;
bufCopy[1] = 0;
bufCopy[2] = 0; /* error register will be set later */
bufCopy[3] = errorBit;
CO_memcpySwap4(&bufCopy[4], &infoCode);
/* copy data to the buffer, increment writePtr and verify buffer full */
CO_LOCK_EMCY();
CO_memcpy(em->bufWritePtr, &bufCopy[0], 8);
em->bufWritePtr += 8;
if(em->bufWritePtr == em->bufEnd) em->bufWritePtr = em->buf;
if(em->bufWritePtr == em->bufReadPtr) em->bufFull = 1;
CO_UNLOCK_EMCY();
/* Optional signal to RTOS, which can resume task, which handles CO_EM_process */
if(em->pFunctSignal != NULL) {
em->pFunctSignal();
}
}
}
}
/******************************************************************************/
bool_t CO_isError(CO_EM_t *em, const uint8_t errorBit){
uint8_t index = errorBit >> 3;
uint8_t bitmask = 1 << (errorBit & 0x7);
bool_t ret = false;
if(em != NULL && index < em->errorStatusBitsSize){
if((em->errorStatusBits[index] & bitmask) != 0){
ret = true;
}
}
return ret;
}

View File

@ -0,0 +1,454 @@
/**
* CANopen Emergency protocol.
*
* @file CO_Emergency.h
* @ingroup CO_Emergency
* @author Janez Paternoster
* @copyright 2004 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CO_EMERGENCY_H
#define CO_EMERGENCY_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup CO_Emergency Emergency
* @ingroup CO_CANopen
* @{
*
* CANopen Emergency protocol.
*
* Error control and Emergency is used for control internal error state
* and for sending a CANopen Emergency message.
*
* In case of error condition stack or application calls CO_errorReport()
* function with indication of the error. Specific error condition is reported
* (with CANopen Emergency message) only the first time after it occurs.
* Internal state of the error condition is controlled with
* @ref CO_EM_errorStatusBits. Specific error condition can also be reset by
* CO_errorReset() function. If so, Emergency message is sent with
* CO_EM_NO_ERROR indication.
*
* Some error conditions are informative and some are critical. Critical error
* conditions sets the #CO_errorRegisterBitmask_t.
*
* Latest errors can be read from _Pre Defined Error Field_ (object dictionary,
* index 0x1003). @ref CO_EM_errorStatusBits can also be read form CANopen
* object dictionary.
*
* ###Emergency message contents:
*
* Byte | Description
* -----|-----------------------------------------------------------
* 0..1 | @ref CO_EM_errorCodes.
* 2 | #CO_errorRegisterBitmask_t.
* 3 | Index of error condition (see @ref CO_EM_errorStatusBits).
* 4..7 | Additional argument informative to CO_errorReport() function.
*
* ####Contents of _Pre Defined Error Field_ (object dictionary, index 0x1003):
* bytes 0..3 are equal to bytes 0..3 in the Emergency message.
*
* @see #CO_Default_CAN_ID_t
*/
/**
* CANopen Error register.
*
* In object dictionary on index 0x1001.
*
* Error register is calculated from critical internal @ref CO_EM_errorStatusBits.
* Generic and communication bits are calculated in CO_EM_process
* function, device profile or manufacturer specific bits may be calculated
* inside the application.
*
* Internal errors may prevent device to stay in NMT Operational state. Details
* are described in _Error Behavior_ object in Object Dictionary at index 0x1029.
*/
typedef enum{
CO_ERR_REG_GENERIC_ERR = 0x01U, /**< bit 0, generic error */
CO_ERR_REG_CURRENT = 0x02U, /**< bit 1, current */
CO_ERR_REG_VOLTAGE = 0x04U, /**< bit 2, voltage */
CO_ERR_REG_TEMPERATURE = 0x08U, /**< bit 3, temperature */
CO_ERR_REG_COMM_ERR = 0x10U, /**< bit 4, communication error (overrun, error state) */
CO_ERR_REG_DEV_PROFILE = 0x20U, /**< bit 5, device profile specific */
CO_ERR_REG_RESERVED = 0x40U, /**< bit 6, reserved (always 0) */
CO_ERR_REG_MANUFACTURER = 0x80U /**< bit 7, manufacturer specific */
}CO_errorRegisterBitmask_t;
/**
* @defgroup CO_EM_errorCodes CANopen Error codes
* @{
*
* Standard error codes according to CiA DS-301 and DS-401.
*/
#define CO_EMC_NO_ERROR 0x0000U /**< 0x00xx, error Reset or No Error */
#define CO_EMC_GENERIC 0x1000U /**< 0x10xx, Generic Error */
#define CO_EMC_CURRENT 0x2000U /**< 0x20xx, Current */
#define CO_EMC_CURRENT_INPUT 0x2100U /**< 0x21xx, Current, device input side */
#define CO_EMC_CURRENT_INSIDE 0x2200U /**< 0x22xx, Current inside the device */
#define CO_EMC_CURRENT_OUTPUT 0x2300U /**< 0x23xx, Current, device output side */
#define CO_EMC_VOLTAGE 0x3000U /**< 0x30xx, Voltage */
#define CO_EMC_VOLTAGE_MAINS 0x3100U /**< 0x31xx, Mains Voltage */
#define CO_EMC_VOLTAGE_INSIDE 0x3200U /**< 0x32xx, Voltage inside the device */
#define CO_EMC_VOLTAGE_OUTPUT 0x3300U /**< 0x33xx, Output Voltage */
#define CO_EMC_TEMPERATURE 0x4000U /**< 0x40xx, Temperature */
#define CO_EMC_TEMP_AMBIENT 0x4100U /**< 0x41xx, Ambient Temperature */
#define CO_EMC_TEMP_DEVICE 0x4200U /**< 0x42xx, Device Temperature */
#define CO_EMC_HARDWARE 0x5000U /**< 0x50xx, Device Hardware */
#define CO_EMC_SOFTWARE_DEVICE 0x6000U /**< 0x60xx, Device Software */
#define CO_EMC_SOFTWARE_INTERNAL 0x6100U /**< 0x61xx, Internal Software */
#define CO_EMC_SOFTWARE_USER 0x6200U /**< 0x62xx, User Software */
#define CO_EMC_DATA_SET 0x6300U /**< 0x63xx, Data Set */
#define CO_EMC_ADDITIONAL_MODUL 0x7000U /**< 0x70xx, Additional Modules */
#define CO_EMC_MONITORING 0x8000U /**< 0x80xx, Monitoring */
#define CO_EMC_COMMUNICATION 0x8100U /**< 0x81xx, Communication */
#define CO_EMC_CAN_OVERRUN 0x8110U /**< 0x8110, CAN Overrun (Objects lost) */
#define CO_EMC_CAN_PASSIVE 0x8120U /**< 0x8120, CAN in Error Passive Mode */
#define CO_EMC_HEARTBEAT 0x8130U /**< 0x8130, Life Guard Error or Heartbeat Error */
#define CO_EMC_BUS_OFF_RECOVERED 0x8140U /**< 0x8140, recovered from bus off */
#define CO_EMC_CAN_ID_COLLISION 0x8150U /**< 0x8150, CAN-ID collision */
#define CO_EMC_PROTOCOL_ERROR 0x8200U /**< 0x82xx, Protocol Error */
#define CO_EMC_PDO_LENGTH 0x8210U /**< 0x8210, PDO not processed due to length error */
#define CO_EMC_PDO_LENGTH_EXC 0x8220U /**< 0x8220, PDO length exceeded */
#define CO_EMC_DAM_MPDO 0x8230U /**< 0x8230, DAM MPDO not processed, destination object not available */
#define CO_EMC_SYNC_DATA_LENGTH 0x8240U /**< 0x8240, Unexpected SYNC data length */
#define CO_EMC_RPDO_TIMEOUT 0x8250U /**< 0x8250, RPDO timeout */
#define CO_EMC_TIME_DATA_LENGTH 0x8260U /**< 0x8260, Unexpected TIME data length */
#define CO_EMC_EXTERNAL_ERROR 0x9000U /**< 0x90xx, External Error */
#define CO_EMC_ADDITIONAL_FUNC 0xF000U /**< 0xF0xx, Additional Functions */
#define CO_EMC_DEVICE_SPECIFIC 0xFF00U /**< 0xFFxx, Device specific */
#define CO_EMC401_OUT_CUR_HI 0x2310U /**< 0x2310, DS401, Current at outputs too high (overload) */
#define CO_EMC401_OUT_SHORTED 0x2320U /**< 0x2320, DS401, Short circuit at outputs */
#define CO_EMC401_OUT_LOAD_DUMP 0x2330U /**< 0x2330, DS401, Load dump at outputs */
#define CO_EMC401_IN_VOLT_HI 0x3110U /**< 0x3110, DS401, Input voltage too high */
#define CO_EMC401_IN_VOLT_LOW 0x3120U /**< 0x3120, DS401, Input voltage too low */
#define CO_EMC401_INTERN_VOLT_HI 0x3210U /**< 0x3210, DS401, Internal voltage too high */
#define CO_EMC401_INTERN_VOLT_LO 0x3220U /**< 0x3220, DS401, Internal voltage too low */
#define CO_EMC401_OUT_VOLT_HIGH 0x3310U /**< 0x3310, DS401, Output voltage too high */
#define CO_EMC401_OUT_VOLT_LOW 0x3320U /**< 0x3320, DS401, Output voltage too low */
/** @} */
/**
* @defgroup CO_EM_errorStatusBits Error status bits
* @{
*
* Internal indication of the error condition.
*
* Each error condition is specified by unique index from 0x00 up to 0xFF.
* Variable (from manufacturer section in the Object
* Dictionary) contains up to 0xFF bits (32bytes) for the identification of the
* specific error condition. (Type of the variable is CANopen OCTET_STRING.)
*
* If specific error occurs in the stack or in the application, CO_errorReport()
* sets specific bit in the _Error Status Bits_ variable. If bit was already
* set, function returns without any action. Otherwise it prepares emergency
* message.
*
* CO_errorReport(), CO_errorReset() or CO_isError() functions are called
* with unique index as an argument. (However CO_errorReport(), for example, may
* be used with the same index on multiple places in the code.)
*
* Macros defined below are combination of two constants: index and
* @ref CO_EM_errorCodes. They represents specific error conditions. They are
* used as double argument for CO_errorReport(), CO_errorReset() and
* CO_isError() functions.
*
* Stack uses first 6 bytes of the _Error Status Bits_ variable. Device profile
* or application may define own macros for Error status bits using
* @ref CO_EM_MANUFACTURER_START and @ref CO_EM_MANUFACTURER_END values. Note that
* _Error Status Bits_ must be large enough (up to 32 bytes).
*/
#define CO_EM_NO_ERROR 0x00U /**< 0x00, Error Reset or No Error */
#define CO_EM_CAN_BUS_WARNING 0x01U /**< 0x01, communication, info, CAN bus warning limit reached */
#define CO_EM_RXMSG_WRONG_LENGTH 0x02U /**< 0x02, communication, info, Wrong data length of the received CAN message */
#define CO_EM_RXMSG_OVERFLOW 0x03U /**< 0x03, communication, info, Previous received CAN message wasn't processed yet */
#define CO_EM_RPDO_WRONG_LENGTH 0x04U /**< 0x04, communication, info, Wrong data length of received PDO */
#define CO_EM_RPDO_OVERFLOW 0x05U /**< 0x05, communication, info, Previous received PDO wasn't processed yet */
#define CO_EM_CAN_RX_BUS_PASSIVE 0x06U /**< 0x06, communication, info, CAN receive bus is passive */
#define CO_EM_CAN_TX_BUS_PASSIVE 0x07U /**< 0x07, communication, info, CAN transmit bus is passive */
#define CO_EM_NMT_WRONG_COMMAND 0x08U /**< 0x08, communication, info, Wrong NMT command received */
#define CO_EM_TIME_TIMEOUT 0x09U /**< 0x09, communication, info, TIME message timeout */
#define CO_EM_TIME_LENGTH 0x0AU /**< 0x0A, communication, info, Unexpected TIME data length */
#define CO_EM_0B_unused 0x0BU /**< 0x0B, (unused) */
#define CO_EM_0C_unused 0x0CU /**< 0x0C, (unused) */
#define CO_EM_0D_unused 0x0DU /**< 0x0D, (unused) */
#define CO_EM_0E_unused 0x0EU /**< 0x0E, (unused) */
#define CO_EM_0F_unused 0x0FU /**< 0x0F, (unused) */
#define CO_EM_10_unused 0x10U /**< 0x10, (unused) */
#define CO_EM_11_unused 0x11U /**< 0x11, (unused) */
#define CO_EM_CAN_TX_BUS_OFF 0x12U /**< 0x12, communication, critical, CAN transmit bus is off */
#define CO_EM_CAN_RXB_OVERFLOW 0x13U /**< 0x13, communication, critical, CAN module receive buffer has overflowed */
#define CO_EM_CAN_TX_OVERFLOW 0x14U /**< 0x14, communication, critical, CAN transmit buffer has overflowed */
#define CO_EM_TPDO_OUTSIDE_WINDOW 0x15U /**< 0x15, communication, critical, TPDO is outside SYNC window */
#define CO_EM_16_unused 0x16U /**< 0x16, (unused) */
#define CO_EM_17_unused 0x17U /**< 0x17, (unused) */
#define CO_EM_SYNC_TIME_OUT 0x18U /**< 0x18, communication, critical, SYNC message timeout */
#define CO_EM_SYNC_LENGTH 0x19U /**< 0x19, communication, critical, Unexpected SYNC data length */
#define CO_EM_PDO_WRONG_MAPPING 0x1AU /**< 0x1A, communication, critical, Error with PDO mapping */
#define CO_EM_HEARTBEAT_CONSUMER 0x1BU /**< 0x1B, communication, critical, Heartbeat consumer timeout */
#define CO_EM_HB_CONSUMER_REMOTE_RESET 0x1CU /**< 0x1C, communication, critical, Heartbeat consumer detected remote node reset */
#define CO_EM_1D_unused 0x1DU /**< 0x1D, (unused) */
#define CO_EM_1E_unused 0x1EU /**< 0x1E, (unused) */
#define CO_EM_1F_unused 0x1FU /**< 0x1F, (unused) */
#define CO_EM_EMERGENCY_BUFFER_FULL 0x20U /**< 0x20, generic, info, Emergency buffer is full, Emergency message wasn't sent */
#define CO_EM_21_unused 0x21U /**< 0x21, (unused) */
#define CO_EM_MICROCONTROLLER_RESET 0x22U /**< 0x22, generic, info, Microcontroller has just started */
#define CO_EM_23_unused 0x23U /**< 0x23, (unused) */
#define CO_EM_24_unused 0x24U /**< 0x24, (unused) */
#define CO_EM_25_unused 0x25U /**< 0x25, (unused) */
#define CO_EM_26_unused 0x26U /**< 0x26, (unused) */
#define CO_EM_27_unused 0x27U /**< 0x27, (unused) */
#define CO_EM_WRONG_ERROR_REPORT 0x28U /**< 0x28, generic, critical, Wrong parameters to CO_errorReport() function */
#define CO_EM_ISR_TIMER_OVERFLOW 0x29U /**< 0x29, generic, critical, Timer task has overflowed */
#define CO_EM_MEMORY_ALLOCATION_ERROR 0x2AU /**< 0x2A, generic, critical, Unable to allocate memory for objects */
#define CO_EM_GENERIC_ERROR 0x2BU /**< 0x2B, generic, critical, Generic error, test usage */
#define CO_EM_GENERIC_SOFTWARE_ERROR 0x2CU /**< 0x2C, generic, critical, Software error */
#define CO_EM_INCONSISTENT_OBJECT_DICT 0x2DU /**< 0x2D, generic, critical, Object dictionary does not match the software */
#define CO_EM_CALCULATION_OF_PARAMETERS 0x2EU /**< 0x2E, generic, critical, Error in calculation of device parameters */
#define CO_EM_NON_VOLATILE_MEMORY 0x2FU /**< 0x2F, generic, critical, Error with access to non volatile device memory */
#define CO_EM_MANUFACTURER_START 0x30U /**< 0x30, manufacturer, info, This can be used by macros to calculate error codes */
#define CO_EM_MANUFACTURER_END 0xFFU /**< 0xFF, manufacturer, info, This can be used by macros to check error codes */
/** @} */
/**
* Size of internal buffer, whwre emergencies are stored after CO_errorReport().
* Buffer is cleared by CO_EM_process().
*/
#define CO_EM_INTERNAL_BUFFER_SIZE 10
/**
* Emergerncy object for CO_errorReport(). It contains error buffer, to which new emergency
* messages are written, when CO_errorReport() is called. This object is included in
* CO_EMpr_t object.
*/
typedef struct{
uint8_t *errorStatusBits; /**< From CO_EM_init() */
uint8_t errorStatusBitsSize; /**< From CO_EM_init() */
/** Internal buffer for storing unsent emergency messages.*/
uint8_t buf[CO_EM_INTERNAL_BUFFER_SIZE * 8];
uint8_t *bufEnd; /**< End+1 address of the above buffer */
uint8_t *bufWritePtr; /**< Write pointer in the above buffer */
uint8_t *bufReadPtr; /**< Read pointer in the above buffer */
uint8_t bufFull; /**< True if above buffer is full */
uint8_t wrongErrorReport; /**< Error in arguments to CO_errorReport() */
/** From CO_EM_initCallback() or NULL */
void (*pFunctSignal)(void);
/** From CO_EM_initCallbackRx() or NULL */
void (*pFunctSignalRx)(const uint16_t ident,
const uint16_t errorCode,
const uint8_t errorRegister,
const uint8_t errorBit,
const uint32_t infoCode);
}CO_EM_t;
/**
* Report error condition.
*
* Function is called on any error condition inside CANopen stack and may also
* be called by application on custom error condition. Emergency message is sent
* after the first occurance of specific error. In case of critical error, device
* will not be able to stay in NMT_OPERATIONAL state.
*
* Function is short and may be used form any task or interrupt.
*
* @param em Emergency object.
* @param errorBit from @ref CO_EM_errorStatusBits.
* @param errorCode from @ref CO_EM_errorCodes.
* @param infoCode 32 bit value is passed to bytes 4...7 of the Emergency message.
* It contains optional additional information inside emergency message.
*/
void CO_errorReport(CO_EM_t *em, const uint8_t errorBit, const uint16_t errorCode, const uint32_t infoCode);
/**
* Reset error condition.
*
* Function is called if any error condition is solved. Emergency message is sent
* with @ref CO_EM_errorCodes 0x0000.
*
* Function is short and may be used form any task or interrupt.
*
* @param em Emergency object.
* @param errorBit from @ref CO_EM_errorStatusBits.
* @param infoCode 32 bit value is passed to bytes 4...7 of the Emergency message.
*/
void CO_errorReset(CO_EM_t *em, const uint8_t errorBit, const uint32_t infoCode);
/**
* Check specific error condition.
*
* Function returns 1, if specific internal error is present. Otherwise it returns 0.
*
* @param em Emergency object.
* @param errorBit from @ref CO_EM_errorStatusBits.
*
* @return false: Error is not present.
* @return true: Error is present.
*/
bool_t CO_isError(CO_EM_t *em, const uint8_t errorBit);
#ifdef CO_DOXYGEN
/** Skip section, if CO_SDO.h is not included */
#define CO_SDO_H
#endif
#ifdef CO_SDO_H
/**
* Error control and Emergency object. It controls internal error state and
* sends emergency message, if error condition was reported. Object is initialized
* by CO_EM_init(). It contains CO_EM_t object.
*/
typedef struct{
uint8_t *errorRegister; /**< From CO_EM_init() */
uint32_t *preDefErr; /**< From CO_EM_init() */
uint8_t preDefErrSize; /**< From CO_EM_init() */
uint8_t preDefErrNoOfErrors;/**< Number of active errors in preDefErr */
uint16_t inhibitEmTimer; /**< Internal timer for emergency message */
CO_EM_t *em; /**< CO_EM_t sub object is included here */
CO_CANmodule_t *CANdev; /**< From CO_EM_init() */
CO_CANtx_t *CANtxBuff; /**< CAN transmit buffer */
}CO_EMpr_t;
/**
* Initialize Error control and Emergency object.
*
* Function must be called in the communication reset section.
*
* @param emPr This object will be initialized.
* @param em Emergency object defined separately. Will be included in emPr and
* initialized too.
* @param SDO SDO server object.
* @param errorStatusBits Pointer to _Error Status Bits_ array from Object Dictionary
* (manufacturer specific section). See @ref CO_EM_errorStatusBits.
* @param errorStatusBitsSize Total size of the above array. Must be >= 6.
* @param errorRegister Pointer to _Error Register_ (Object dictionary, index 0x1001).
* @param preDefErr Pointer to _Pre defined error field_ array from Object
* dictionary, index 0x1003.
* @param preDefErrSize Size of the above array.
* @param CANdevRx CAN device for Emergency reception.
* @param CANdevRxIdx Index of receive buffer in the above CAN device.
* @param CANdevTx CAN device for Emergency transmission.
* @param CANdevTxIdx Index of transmit buffer in the above CAN device.
* @param CANidTxEM CAN identifier for Emergency message.
*
* @return #CO_ReturnError_t CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
*/
CO_ReturnError_t CO_EM_init(
CO_EM_t *em,
CO_EMpr_t *emPr,
CO_SDO_t *SDO,
uint8_t *errorStatusBits,
uint8_t errorStatusBitsSize,
uint8_t *errorRegister,
uint32_t *preDefErr,
uint8_t preDefErrSize,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdx,
CO_CANmodule_t *CANdevTx,
uint16_t CANdevTxIdx,
uint16_t CANidTxEM);
/**
* Initialize Emergency callback function.
*
* Function initializes optional callback function, which executes after
* error condition is changed. Function may wake up external task,
* which processes mainline CANopen functions.
*
* @param em This object.
* @param pFunctSignal Pointer to the callback function. Not called if NULL.
*/
void CO_EM_initCallback(
CO_EM_t *em,
void (*pFunctSignal)(void));
/**
* Initialize Emergency received callback function.
*
* Function initializes optional callback function, which executes after
* error condition is received. Function may wake up external task,
* which processes mainline CANopen functions.
*
* @remark Depending on the CAN driver implementation, this function is called
* inside an ISR
*
* @param em This object.
* @param pFunctSignal Pointer to the callback function. Not called if NULL.
*/
void CO_EM_initCallbackRx(
CO_EM_t *em,
void (*pFunctSignalRx)(const uint16_t ident,
const uint16_t errorCode,
const uint8_t errorRegister,
const uint8_t errorBit,
const uint32_t infoCode));
/**
* Process Error control and Emergency object.
*
* Function must be called cyclically. It verifies some communication errors,
* calculates bit 0 and bit 4 from _Error register_ and sends emergency message
* if necessary.
*
* @param emPr This object.
* @param NMTisPreOrOperational True if this node is NMT_PRE_OPERATIONAL or NMT_OPERATIONAL.
* @param timeDifference_100us Time difference from previous function call in [100 * microseconds].
* @param emInhTime _Inhibit time EMCY_ (object dictionary, index 0x1015).
* @param timerNext_ms Return value - info to OS - see CO_process().
*/
void CO_EM_process(
CO_EMpr_t *emPr,
bool_t NMTisPreOrOperational,
uint16_t timeDifference_100us,
uint16_t emInhTime,
uint16_t *timerNext_ms);
#endif
#ifdef __cplusplus
}
#endif /*__cplusplus*/
/** @} */
#endif

View File

@ -0,0 +1,420 @@
/*
* CANopen Heartbeat consumer object.
*
* @file CO_HBconsumer.c
* @ingroup CO_HBconsumer
* @author Janez Paternoster
* @copyright 2004 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "CANopen.h"
#include "CO_HBconsumer.h"
/*
* Read received message from CAN module.
*
* Function will be called (by CAN receive interrupt) every time, when CAN
* message with correct identifier will be received. For more information and
* description of parameters see file CO_driver.h.
*/
static void CO_HBcons_receive(void *object, const CO_CANrxMsg_t *msg){
CO_HBconsNode_t *HBconsNode;
HBconsNode = (CO_HBconsNode_t*) object; /* this is the correct pointer type of the first argument */
/* verify message length */
if(msg->DLC == 1){
/* copy data and set 'new message' flag. */
HBconsNode->NMTstate = (CO_NMT_internalState_t)msg->data[0];
SET_CANrxNew(HBconsNode->CANrxNew);
}
}
/*
* Configure one monitored node.
*/
static void CO_HBcons_monitoredNodeConfig(
CO_HBconsumer_t *HBcons,
uint8_t idx,
uint8_t nodeId,
uint16_t time)
{
uint16_t COB_ID;
CO_HBconsNode_t *monitoredNode;
if(idx >= HBcons->numberOfMonitoredNodes) return;
monitoredNode = &HBcons->monitoredNodes[idx];
monitoredNode->nodeId = nodeId;
monitoredNode->time = time;
monitoredNode->NMTstate = CO_NMT_INITIALIZING;
monitoredNode->HBstate = CO_HBconsumer_UNCONFIGURED;
/* is channel used */
if(monitoredNode->nodeId && monitoredNode->time){
COB_ID = monitoredNode->nodeId + CO_CAN_ID_HEARTBEAT;
monitoredNode->HBstate = CO_HBconsumer_UNKNOWN;
}
else{
COB_ID = 0;
monitoredNode->time = 0;
}
/* configure Heartbeat consumer CAN reception */
if (monitoredNode->HBstate != CO_HBconsumer_UNCONFIGURED) {
CO_CANrxBufferInit(
HBcons->CANdevRx,
HBcons->CANdevRxIdxStart + idx,
COB_ID,
0x7FF,
0,
(void*)&HBcons->monitoredNodes[idx],
CO_HBcons_receive);
}
}
/*
* OD function for accessing _Consumer Heartbeat Time_ (index 0x1016) from SDO server.
*
* For more information see file CO_SDO.h.
*/
static CO_SDO_abortCode_t CO_ODF_1016(CO_ODF_arg_t *ODF_arg)
{
CO_HBconsumer_t *HBcons;
uint8_t NodeID;
uint16_t HBconsTime;
uint32_t value;
CO_ReturnError_t ret;
if(ODF_arg->reading){
return CO_SDO_AB_NONE;
}
HBcons = (CO_HBconsumer_t*) ODF_arg->object;
value = CO_getUint32(ODF_arg->data);
NodeID = (value >> 16U) & 0xFFU;
HBconsTime = value & 0xFFFFU;
if((value & 0xFF800000U) != 0){
return CO_SDO_AB_PRAM_INCOMPAT;
}
ret = CO_HBconsumer_initEntry(HBcons, ODF_arg->subIndex-1U, NodeID, HBconsTime);
if (ret != CO_ERROR_NO) {
return CO_SDO_AB_PRAM_INCOMPAT;
}
return CO_SDO_AB_NONE;
}
/******************************************************************************/
CO_ReturnError_t CO_HBconsumer_init(
CO_HBconsumer_t *HBcons,
CO_EM_t *em,
CO_SDO_t *SDO,
const uint32_t HBconsTime[],
CO_HBconsNode_t monitoredNodes[],
uint8_t numberOfMonitoredNodes,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdxStart)
{
uint8_t i;
/* verify arguments */
if(HBcons==NULL || em==NULL || SDO==NULL || HBconsTime==NULL ||
monitoredNodes==NULL || CANdevRx==NULL){
return CO_ERROR_ILLEGAL_ARGUMENT;
}
/* Configure object variables */
HBcons->em = em;
HBcons->HBconsTime = HBconsTime;
HBcons->monitoredNodes = monitoredNodes;
HBcons->numberOfMonitoredNodes = numberOfMonitoredNodes;
HBcons->allMonitoredOperational = 0;
HBcons->CANdevRx = CANdevRx;
HBcons->CANdevRxIdxStart = CANdevRxIdxStart;
for(i=0; i<HBcons->numberOfMonitoredNodes; i++) {
uint8_t nodeId = (HBcons->HBconsTime[i] >> 16U) & 0xFFU;
uint16_t time = HBcons->HBconsTime[i] & 0xFFFFU;
CO_HBconsumer_initEntry(HBcons, i, nodeId, time);
}
/* Configure Object dictionary entry at index 0x1016 */
CO_OD_configure(SDO, OD_H1016_CONSUMER_HB_TIME, CO_ODF_1016, (void*)HBcons, 0, 0);
return CO_ERROR_NO;
}
/******************************************************************************/
CO_ReturnError_t CO_HBconsumer_initEntry(
CO_HBconsumer_t *HBcons,
uint8_t idx,
uint8_t nodeId,
uint16_t consumerTime)
{
CO_ReturnError_t ret = CO_ERROR_NO;
/* verify arguments */
if(HBcons==NULL){
return CO_ERROR_ILLEGAL_ARGUMENT;
}
if((consumerTime != 0) && (nodeId != 0)){
uint8_t i;
/* there must not be more entries with same index and time different than zero */
for(i = 0U; i<HBcons->numberOfMonitoredNodes; i++){
uint32_t objectCopy = HBcons->HBconsTime[i];
uint8_t NodeIDObj = (objectCopy >> 16U) & 0xFFU;
uint16_t HBconsTimeObj = objectCopy & 0xFFFFU;
if((idx != i) && (HBconsTimeObj != 0) && (nodeId == NodeIDObj)){
ret = CO_ERROR_ILLEGAL_ARGUMENT;
}
}
}
/* Configure */
if(ret == CO_ERROR_NO){
CO_HBcons_monitoredNodeConfig(HBcons, idx, nodeId, consumerTime);
}
return ret;
}
/******************************************************************************/
void CO_HBconsumer_initCallbackHeartbeatStarted(
CO_HBconsumer_t *HBcons,
uint8_t idx,
void *object,
void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object))
{
CO_HBconsNode_t *monitoredNode;
if (HBcons==NULL || idx>HBcons->numberOfMonitoredNodes) {
return;
}
monitoredNode = &HBcons->monitoredNodes[idx];
monitoredNode->pFunctSignalHbStarted = pFunctSignal;
monitoredNode->functSignalObjectHbStarted = object;
}
/******************************************************************************/
void CO_HBconsumer_initCallbackTimeout(
CO_HBconsumer_t *HBcons,
uint8_t idx,
void *object,
void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object))
{
CO_HBconsNode_t *monitoredNode;
if (HBcons==NULL || idx>HBcons->numberOfMonitoredNodes) {
return;
}
monitoredNode = &HBcons->monitoredNodes[idx];
monitoredNode->pFunctSignalTimeout = pFunctSignal;
monitoredNode->functSignalObjectTimeout = object;
}
/******************************************************************************/
void CO_HBconsumer_initCallbackRemoteReset(
CO_HBconsumer_t *HBcons,
uint8_t idx,
void *object,
void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object))
{
CO_HBconsNode_t *monitoredNode;
if (HBcons==NULL || idx>HBcons->numberOfMonitoredNodes) {
return;
}
monitoredNode = &HBcons->monitoredNodes[idx];
monitoredNode->pFunctSignalRemoteReset = pFunctSignal;
monitoredNode->functSignalObjectRemoteReset = object;
}
/******************************************************************************/
void CO_HBconsumer_process(
CO_HBconsumer_t *HBcons,
bool_t NMTisPreOrOperational,
uint16_t timeDifference_ms)
{
uint8_t i;
uint8_t emcyHeartbeatTimeoutActive = 0;
uint8_t emcyRemoteResetActive = 0;
uint8_t AllMonitoredOperationalCopy;
CO_HBconsNode_t *monitoredNode;
AllMonitoredOperationalCopy = 5;
monitoredNode = &HBcons->monitoredNodes[0];
if(NMTisPreOrOperational){
for(i=0; i<HBcons->numberOfMonitoredNodes; i++){
if(monitoredNode->time > 0){/* is node monitored */
/* Verify if received message is heartbeat or bootup */
if(IS_CANrxNew(monitoredNode->CANrxNew)){
if(monitoredNode->NMTstate == CO_NMT_INITIALIZING){
/* bootup message, call callback */
if (monitoredNode->pFunctSignalRemoteReset != NULL) {
monitoredNode->pFunctSignalRemoteReset(monitoredNode->nodeId, i,
monitoredNode->functSignalObjectRemoteReset);
}
}
else {
/* heartbeat message */
if (monitoredNode->HBstate!=CO_HBconsumer_ACTIVE &&
monitoredNode->pFunctSignalHbStarted!=NULL) {
monitoredNode->pFunctSignalHbStarted(monitoredNode->nodeId, i,
monitoredNode->functSignalObjectHbStarted);
}
monitoredNode->HBstate = CO_HBconsumer_ACTIVE;
monitoredNode->timeoutTimer = 0; /* reset timer */
timeDifference_ms = 0;
}
CLEAR_CANrxNew(monitoredNode->CANrxNew);
}
/* Verify timeout */
if(monitoredNode->timeoutTimer < monitoredNode->time) {
monitoredNode->timeoutTimer += timeDifference_ms;
}
if(monitoredNode->HBstate!=CO_HBconsumer_UNCONFIGURED &&
monitoredNode->HBstate!=CO_HBconsumer_UNKNOWN) {
if(monitoredNode->timeoutTimer >= monitoredNode->time){
/* timeout expired */
CO_errorReport(HBcons->em, CO_EM_HEARTBEAT_CONSUMER, CO_EMC_HEARTBEAT, i);
emcyHeartbeatTimeoutActive = 1;
monitoredNode->NMTstate = CO_NMT_INITIALIZING;
if (monitoredNode->HBstate!=CO_HBconsumer_TIMEOUT &&
monitoredNode->pFunctSignalTimeout!=NULL) {
monitoredNode->pFunctSignalTimeout(monitoredNode->nodeId, i,
monitoredNode->functSignalObjectTimeout);
}
monitoredNode->HBstate = CO_HBconsumer_TIMEOUT;
}
else if(monitoredNode->NMTstate == CO_NMT_INITIALIZING){
/* there was a bootup message */
CO_errorReport(HBcons->em, CO_EM_HB_CONSUMER_REMOTE_RESET, CO_EMC_HEARTBEAT, i);
emcyRemoteResetActive = 1;
monitoredNode->HBstate = CO_HBconsumer_UNKNOWN;
}
}
if(monitoredNode->NMTstate != CO_NMT_OPERATIONAL) {
AllMonitoredOperationalCopy = 0;
}
}
monitoredNode++;
}
}
else{ /* not in (pre)operational state */
for(i=0; i<HBcons->numberOfMonitoredNodes; i++){
monitoredNode->NMTstate = CO_NMT_INITIALIZING;
CLEAR_CANrxNew(monitoredNode->CANrxNew);
if(monitoredNode->HBstate != CO_HBconsumer_UNCONFIGURED){
monitoredNode->HBstate = CO_HBconsumer_UNKNOWN;
}
monitoredNode++;
}
AllMonitoredOperationalCopy = 0;
}
/* clear emergencies. We only have one emergency index for all
* monitored nodes! */
if ( ! emcyHeartbeatTimeoutActive) {
CO_errorReset(HBcons->em, CO_EM_HEARTBEAT_CONSUMER, 0);
}
if ( ! emcyRemoteResetActive) {
CO_errorReset(HBcons->em, CO_EM_HB_CONSUMER_REMOTE_RESET, 0);
}
HBcons->allMonitoredOperational = AllMonitoredOperationalCopy;
}
/******************************************************************************/
int8_t CO_HBconsumer_getIdxByNodeId(
CO_HBconsumer_t *HBcons,
uint8_t nodeId)
{
uint8_t i;
CO_HBconsNode_t *monitoredNode;
if (HBcons == NULL) {
return -1;
}
/* linear search for the node */
monitoredNode = &HBcons->monitoredNodes[0];
for(i=0; i<HBcons->numberOfMonitoredNodes; i++){
if (monitoredNode->nodeId == nodeId) {
return i;
}
monitoredNode ++;
}
/* not found */
return -1;
}
/******************************************************************************/
CO_HBconsumer_state_t CO_HBconsumer_getState(
CO_HBconsumer_t *HBcons,
uint8_t idx)
{
CO_HBconsNode_t *monitoredNode;
if (HBcons==NULL || idx>HBcons->numberOfMonitoredNodes) {
return CO_HBconsumer_UNCONFIGURED;
}
monitoredNode = &HBcons->monitoredNodes[idx];
return monitoredNode->HBstate;
}
/******************************************************************************/
int8_t CO_HBconsumer_getNmtState(
CO_HBconsumer_t *HBcons,
uint8_t idx,
CO_NMT_internalState_t *nmtState)
{
CO_HBconsNode_t *monitoredNode;
if (HBcons==NULL || nmtState==NULL || idx>HBcons->numberOfMonitoredNodes) {
return -1;
}
*nmtState = CO_NMT_INITIALIZING;
monitoredNode = &HBcons->monitoredNodes[idx];
if (monitoredNode->HBstate == CO_HBconsumer_ACTIVE) {
*nmtState = monitoredNode->NMTstate;
return 0;
}
return -1;
}

View File

@ -0,0 +1,267 @@
/**
* CANopen Heartbeat consumer protocol.
*
* @file CO_HBconsumer.h
* @ingroup CO_HBconsumer
* @author Janez Paternoster
* @copyright 2004 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CO_HB_CONS_H
#define CO_HB_CONS_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup CO_HBconsumer Heartbeat consumer
* @ingroup CO_CANopen
* @{
*
* CANopen Heartbeat consumer protocol.
*
* Heartbeat consumer monitors Heartbeat messages from remote nodes. If any
* monitored node don't send his Heartbeat in specified time, Heartbeat consumer
* sends emergency message. If all monitored nodes are operational, then
* variable _allMonitoredOperational_ inside CO_HBconsumer_t is set to true.
* Monitoring starts after the reception of the first HeartBeat (not bootup).
*
* Heartbeat set up is done by writing to the OD registers 0x1016 or by using
* the function _CO_HBconsumer_initEntry()_
*
* @see @ref CO_NMT_Heartbeat
*/
/**
* Heartbeat state of a node
*/
typedef enum {
CO_HBconsumer_UNCONFIGURED = 0x00U, /**< Consumer entry inactive */
CO_HBconsumer_UNKNOWN = 0x01U, /**< Consumer enabled, but no heartbeat received yet */
CO_HBconsumer_ACTIVE = 0x02U, /**< Heartbeat received within set time */
CO_HBconsumer_TIMEOUT = 0x03U, /**< No heatbeat received for set time */
} CO_HBconsumer_state_t;
/**
* One monitored node inside CO_HBconsumer_t.
*/
typedef struct{
uint8_t nodeId; /**< Node Id of the monitored node */
CO_NMT_internalState_t NMTstate; /**< Of the remote node (Heartbeat payload) */
CO_HBconsumer_state_t HBstate; /**< Current heartbeat state */
uint16_t timeoutTimer; /**< Time since last heartbeat received */
uint16_t time; /**< Consumer heartbeat time from OD */
volatile void *CANrxNew; /**< Indication if new Heartbeat message received from the CAN bus */
/** Callback for heartbeat state change to active event */
void (*pFunctSignalHbStarted)(uint8_t nodeId, uint8_t idx, void *object); /**< From CO_HBconsumer_initTimeoutCallback() or NULL */
void *functSignalObjectHbStarted;/**< Pointer to object */
/** Callback for consumer timeout event */
void (*pFunctSignalTimeout)(uint8_t nodeId, uint8_t idx, void *object); /**< From CO_HBconsumer_initTimeoutCallback() or NULL */
void *functSignalObjectTimeout;/**< Pointer to object */
/** Callback for remote reset event */
void (*pFunctSignalRemoteReset)(uint8_t nodeId, uint8_t idx, void *object); /**< From CO_HBconsumer_initRemoteResetCallback() or NULL */
void *functSignalObjectRemoteReset;/**< Pointer to object */
}CO_HBconsNode_t;
/**
* Heartbeat consumer object.
*
* Object is initilaized by CO_HBconsumer_init(). It contains an array of
* CO_HBconsNode_t objects.
*/
typedef struct{
CO_EM_t *em; /**< From CO_HBconsumer_init() */
const uint32_t *HBconsTime; /**< From CO_HBconsumer_init() */
CO_HBconsNode_t *monitoredNodes; /**< From CO_HBconsumer_init() */
uint8_t numberOfMonitoredNodes; /**< From CO_HBconsumer_init() */
/** True, if all monitored nodes are NMT operational or no node is
monitored. Can be read by the application */
uint8_t allMonitoredOperational;
CO_CANmodule_t *CANdevRx; /**< From CO_HBconsumer_init() */
uint16_t CANdevRxIdxStart; /**< From CO_HBconsumer_init() */
}CO_HBconsumer_t;
/**
* Initialize Heartbeat consumer object.
*
* Function must be called in the communication reset section.
*
* @param HBcons This object will be initialized.
* @param em Emergency object.
* @param SDO SDO server object.
* @param HBconsTime Pointer to _Consumer Heartbeat Time_ array
* from Object Dictionary (index 0x1016). Size of array is equal to numberOfMonitoredNodes.
* @param monitoredNodes Pointer to the externaly defined array of the same size
* as numberOfMonitoredNodes.
* @param numberOfMonitoredNodes Total size of the above arrays.
* @param CANdevRx CAN device for Heartbeat reception.
* @param CANdevRxIdxStart Starting index of receive buffer in the above CAN device.
* Number of used indexes is equal to numberOfMonitoredNodes.
*
* @return #CO_ReturnError_t CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
*/
CO_ReturnError_t CO_HBconsumer_init(
CO_HBconsumer_t *HBcons,
CO_EM_t *em,
CO_SDO_t *SDO,
const uint32_t HBconsTime[],
CO_HBconsNode_t monitoredNodes[],
uint8_t numberOfMonitoredNodes,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdxStart);
/**
* Initialize one Heartbeat consumer entry
*
* Calling this function has the same affect as writing to the corresponding
* entries in the Object Dictionary (index 0x1016)
* @remark The values in the Object Dictionary must be set manually by the
* calling function so that heartbeat consumer behaviour matches the OD value.
*
* @param HBcons This object.
* @param idx index of the node in HBcons object
* @param nodeId see OD 0x1016 description
* @param consumerTime see OD 0x1016 description
* @return
*/
CO_ReturnError_t CO_HBconsumer_initEntry(
CO_HBconsumer_t *HBcons,
uint8_t idx,
uint8_t nodeId,
uint16_t consumerTime);
/**
* Initialize Heartbeat consumer started callback function.
*
* Function initializes optional callback function, which is called for the
* first received heartbeat after activating hb consumer or timeout.
* Function may wake up external task, which handles this event.
*
* @param HBcons This object.
* @param idx index of the node in HBcons object
* @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL
* @param pFunctSignal Pointer to the callback function. Not called if NULL.
*/
void CO_HBconsumer_initCallbackHeartbeatStarted(
CO_HBconsumer_t *HBcons,
uint8_t idx,
void *object,
void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object));
/**
* Initialize Heartbeat consumer timeout callback function.
*
* Function initializes optional callback function, which is called when the node
* state changes from active to timeout. Function may wake up external task,
* which handles this event.
*
* @param HBcons This object.
* @param idx index of the node in HBcons object
* @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL
* @param pFunctSignal Pointer to the callback function. Not called if NULL.
*/
void CO_HBconsumer_initCallbackTimeout(
CO_HBconsumer_t *HBcons,
uint8_t idx,
void *object,
void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object));
/**
* Initialize Heartbeat consumer remote reset detected callback function.
*
* Function initializes optional callback function, which is called when a bootup
* message is received from the remote node. Function may wake up external task,
* which handles this event.
*
* @param HBcons This object.
* @param idx index of the node in HBcons object
* @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL
* @param pFunctSignal Pointer to the callback function. Not called if NULL.
*/
void CO_HBconsumer_initCallbackRemoteReset(
CO_HBconsumer_t *HBcons,
uint8_t idx,
void *object,
void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object));
/**
* Process Heartbeat consumer object.
*
* Function must be called cyclically.
*
* @param HBcons This object.
* @param NMTisPreOrOperational True if this node is NMT_PRE_OPERATIONAL or NMT_OPERATIONAL.
* @param timeDifference_ms Time difference from previous function call in [milliseconds].
*/
void CO_HBconsumer_process(
CO_HBconsumer_t *HBcons,
bool_t NMTisPreOrOperational,
uint16_t timeDifference_ms);
/**
* Get the heartbeat producer object index by node ID
*
* @param HBcons This object.
* @param nodeId producer node ID
* @return index. -1 if not found
*/
int8_t CO_HBconsumer_getIdxByNodeId(
CO_HBconsumer_t *HBcons,
uint8_t nodeId);
/**
* Get the current state of a heartbeat producer by the index in OD 0x1016
*
* @param HBcons This object.
* @param idx object sub index
* @return #CO_HBconsumer_state_t
*/
CO_HBconsumer_state_t CO_HBconsumer_getState(
CO_HBconsumer_t *HBcons,
uint8_t idx);
/**
* Get the current NMT state of a heartbeat producer by the index in OD 0x1016
*
* NMT state is only available when heartbeat is enabled for this index!
*
* @param HBcons This object.
* @param idx object sub index
* @param [out] #CO_NMT_internalState_t of this index
* @retval 0 NMT state has been received and is valid
* @retval -1 not valid
*/
int8_t CO_HBconsumer_getNmtState(
CO_HBconsumer_t *HBcons,
uint8_t idx,
CO_NMT_internalState_t *nmtState);
#ifdef __cplusplus
}
#endif /*__cplusplus*/
/** @} */
#endif

253
components/canopen/CO_LSS.h Normal file
View File

@ -0,0 +1,253 @@
/**
* CANopen LSS Master/Slave protocol.
*
* @file CO_LSS.h
* @ingroup CO_LSS
* @author Martin Wagner
* @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH
*
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CO_LSS_H
#define CO_LSS_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup CO_LSS LSS
* @ingroup CO_CANopen
* @{
*
* CANopen Layer Setting Services protocol
*
* LSS protocol is according to CiA DSP 305 V3.0.0.
*
* LSS services and protocols are used to inquire or to change the settings
* of three parameters of the physical layer, data link layer, and application
* layer on a CANopen device with LSS slave capability by a CANopen device
* with LSS master capability via the CAN network.
*
* The following parameters may be inquired or changed:
* - Node-ID of the CANopen device
* - Bit timing parameters of the physical layer (bit rate)
* - LSS address compliant to the identity object (1018h)
*
* The connection is established in one of two ways:
* - addressing a node by it's 128 bit LSS address. This requires that the
* master already knows the node's LSS address.
* - scanning the network for unknown nodes (Fastscan). Using this method,
* unknown devices can be found and configured one by one.
*
* Be aware that changing the bit rate is a critical step for the network. A
* failure will render the network unusable!
*
* Using this implementation, only master or slave can be included in one
* node at a time.
*
* For CAN identifiers see #CO_Default_CAN_ID_t
*/
#if CO_NO_LSS_CLIENT == 1 || CO_NO_LSS_SERVER == 1
/**
* LSS protocol command specifiers
*
* The LSS protocols are executed between the LSS master device and the LSS
* slave device(s) to implement the LSS services. Some LSS protocols require
* a sequence of CAN messages.
*
* As identifying method only "LSS fastscan" is supported.
*/
typedef enum {
CO_LSS_SWITCH_STATE_GLOBAL = 0x04U, /**< Switch state global protocol */
CO_LSS_SWITCH_STATE_SEL_VENDOR = 0x40U, /**< Switch state selective protocol - Vendor ID */
CO_LSS_SWITCH_STATE_SEL_PRODUCT = 0x41U, /**< Switch state selective protocol - Product code */
CO_LSS_SWITCH_STATE_SEL_REV = 0x42U, /**< Switch state selective protocol - Revision number */
CO_LSS_SWITCH_STATE_SEL_SERIAL = 0x43U, /**< Switch state selective protocol - Serial number */
CO_LSS_SWITCH_STATE_SEL = 0x44U, /**< Switch state selective protocol - Slave response */
CO_LSS_CFG_NODE_ID = 0x11U, /**< Configure node ID protocol */
CO_LSS_CFG_BIT_TIMING = 0x13U, /**< Configure bit timing parameter protocol */
CO_LSS_CFG_ACTIVATE_BIT_TIMING = 0x15U, /**< Activate bit timing parameter protocol */
CO_LSS_CFG_STORE = 0x17U, /**< Store configuration protocol */
CO_LSS_IDENT_SLAVE = 0x4FU, /**< LSS Fastscan response */
CO_LSS_IDENT_FASTSCAN = 0x51U, /**< LSS Fastscan protocol */
CO_LSS_INQUIRE_VENDOR = 0x5AU, /**< Inquire identity vendor-ID protocol */
CO_LSS_INQUIRE_PRODUCT = 0x5BU, /**< Inquire identity product-code protocol */
CO_LSS_INQUIRE_REV = 0x5CU, /**< Inquire identity revision-number protocol */
CO_LSS_INQUIRE_SERIAL = 0x5DU, /**< Inquire identity serial-number protocol */
CO_LSS_INQUIRE_NODE_ID = 0x5EU, /**< Inquire node-ID protocol */
} CO_LSS_cs_t;
/**
* Macro to get service type group from command specifier
* @{*/
#define CO_LSS_CS_SERVICE_IS_SWITCH_GLOBAL(cs) (cs == CO_LSS_SWITCH_STATE_GLOBAL)
#define CO_LSS_CS_SERVICE_IS_SWITCH_STATE_SELECTIVE(cs) (cs >= CO_LSS_SWITCH_STATE_SEL_VENDOR && cs <= CO_LSS_SWITCH_STATE_SEL)
#define CO_LSS_CS_SERVICE_IS_CONFIG(cs) (cs >= CO_LSS_CFG_NODE_ID && cs <= CO_LSS_CFG_STORE)
#define CO_LSS_CS_SERVICE_IS_INQUIRE(cs) (cs >= CO_LSS_INQUIRE_VENDOR && cs <= CO_LSS_INQUIRE_NODE_ID)
#define CO_LSS_CS_SERVICE_IS_IDENT(cs) (cs==CO_LSS_IDENT_SLAVE || cs==CO_LSS_IDENT_FASTSCAN)
/**@}*/
/**
* Error codes for Configure node ID protocol
*/
typedef enum {
CO_LSS_CFG_NODE_ID_OK = 0x00U,/**< Protocol successfully completed */
CO_LSS_CFG_NODE_ID_OUT_OF_RANGE = 0x01U,/**< NID out of range */
CO_LSS_CFG_NODE_ID_MANUFACTURER = 0xFFU /**< Manufacturer specific error. No further support */
} CO_LSS_cfgNodeId_t;
/**
* Error codes for Configure bit timing parameters protocol
*/
typedef enum {
CO_LSS_CFG_BIT_TIMING_OK = 0x00U,/**< Protocol successfully completed */
CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE = 0x01U,/**< Bit timing / Bit rate not supported */
CO_LSS_CFG_BIT_TIMING_MANUFACTURER = 0xFFU /**< Manufacturer specific error. No further support */
} CO_LSS_cfgBitTiming_t;
/**
* Error codes for Store configuration protocol
*/
typedef enum {
CO_LSS_CFG_STORE_OK = 0x00U, /**< Protocol successfully completed */
CO_LSS_CFG_STORE_NOT_SUPPORTED = 0x01U, /**< Store configuration not supported */
CO_LSS_CFG_STORE_FAILED = 0x02U, /**< Storage media access error */
CO_LSS_CFG_STORE_MANUFACTURER = 0xFFU /**< Manufacturer specific error. No further support */
} CO_LSS_cfgStore_t;
/**
* Fastscan BitCheck. BIT0 means all bits are checked for equality by slave.
*/
typedef enum {
CO_LSS_FASTSCAN_BIT0 = 0x00U, /**< Least significant bit of IDnumbners bit area to be checked */
/* ... */
CO_LSS_FASTSCAN_BIT31 = 0x1FU, /**< dito */
CO_LSS_FASTSCAN_CONFIRM = 0x80U /**< All LSS slaves waiting for scan respond and previous scan is reset */
} CO_LSS_fastscan_bitcheck;
#define CO_LSS_FASTSCAN_BITCHECK_VALID(bit) ((bit>=CO_LSS_FASTSCAN_BIT0 && bit<=CO_LSS_FASTSCAN_BIT31) || bit==CO_LSS_FASTSCAN_CONFIRM)
/**
* Fastscan LSSsub, LSSnext
*/
typedef enum {
CO_LSS_FASTSCAN_VENDOR_ID = 0, /**< Vendor ID */
CO_LSS_FASTSCAN_PRODUCT = 1, /**< Product code */
CO_LSS_FASTSCAN_REV = 2, /**< Revision number */
CO_LSS_FASTSCAN_SERIAL = 3 /**< Serial number */
} CO_LSS_fastscan_lss_sub_next;
#define CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(index) (index>=CO_LSS_FASTSCAN_VENDOR_ID && index<=CO_LSS_FASTSCAN_SERIAL)
/**
* The LSS address is a 128 bit number, uniquely identifying each node. It
* consists of the values in object 0x1018.
*/
typedef union {
uint32_t addr[4];
struct {
uint32_t vendorID;
uint32_t productCode;
uint32_t revisionNumber;
uint32_t serialNumber;
} identity;
} CO_LSS_address_t;
/**
* LSS finite state automaton
*
* The LSS FSA shall provide the following states:
* - Initial: Pseudo state, indicating the activation of the FSA.
* - LSS waiting: In this state, the LSS slave device waits for requests.
* - LSS configuration: In this state variables may be configured in the LSS slave.
* - Final: Pseudo state, indicating the deactivation of the FSA.
*/
typedef enum {
CO_LSS_STATE_WAITING = 0, /**< LSS FSA waiting for requests*/
CO_LSS_STATE_CONFIGURATION = 1, /**< LSS FSA waiting for configuration*/
} CO_LSS_state_t;
/**
* Definition of table_index for /CiA301/ bit timing table
*/
typedef enum {
CO_LSS_BIT_TIMING_1000 = 0, /**< 1000kbit/s */
CO_LSS_BIT_TIMING_800 = 1, /**< 800kbit/s */
CO_LSS_BIT_TIMING_500 = 2, /**< 500kbit/s */
CO_LSS_BIT_TIMING_250 = 3, /**< 250kbit/s */
CO_LSS_BIT_TIMING_125 = 4, /**< 125kbit/s */
/* reserved = 5 */
CO_LSS_BIT_TIMING_50 = 6, /**< 50kbit/s */
CO_LSS_BIT_TIMING_20 = 7, /**< 20kbit/s */
CO_LSS_BIT_TIMING_10 = 8, /**< 10kbit/s */
CO_LSS_BIT_TIMING_AUTO = 9, /**< Automatic bit rate detection */
} CO_LSS_bitTimingTable_t;
/**
* Lookup table for conversion between bit timing table and numerical
* bit rate
*/
static const uint16_t CO_LSS_bitTimingTableLookup[] = {
1000,
800,
500,
250,
125,
0,
50,
20,
10,
0
};
/**
* Macro to check if index contains valid bit timing
*/
#define CO_LSS_BIT_TIMING_VALID(index) (index != 5 && (index >= CO_LSS_BIT_TIMING_1000 && index <= CO_LSS_BIT_TIMING_AUTO))
/**
* Invalid node ID triggers node ID assignment
*/
#define CO_LSS_NODE_ID_ASSIGNMENT 0xFFU
/**
* Macro to check if node id is valid
*/
#define CO_LSS_NODE_ID_VALID(nid) ((nid >= 1 && nid <= 0x7F) || nid == CO_LSS_NODE_ID_ASSIGNMENT)
/**
* Macro to check if two LSS addresses are equal
*/
#define CO_LSS_ADDRESS_EQUAL(/*CO_LSS_address_t*/ a1, /*CO_LSS_address_t*/ a2) \
(a1.identity.productCode == a2.identity.productCode && \
a1.identity.revisionNumber == a2.identity.revisionNumber && \
a1.identity.serialNumber == a2.identity.serialNumber && \
a1.identity.vendorID == a2.identity.vendorID)
#endif /* CO_NO_LSS_CLIENT == 1 || CO_NO_LSS_SERVER == 1 */
#ifdef __cplusplus
}
#endif /*__cplusplus*/
/** @} */
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,484 @@
/**
* CANopen LSS Master/Slave protocol.
*
* @file CO_LSSmaster.h
* @ingroup CO_LSS
* @author Martin Wagner
* @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH
*
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CO_LSSmaster_H
#define CO_LSSmaster_H
#ifdef __cplusplus
extern "C" {
#endif
#if CO_NO_LSS_CLIENT == 1
#include "CO_LSS.h"
/**
* @addtogroup CO_LSS
* @defgroup CO_LSSmaster LSS Master
* @ingroup CO_LSS
* @{
*
* CANopen Layer Setting Service - client protocol
*
* The client/master can use the following services
* - node selection via LSS address
* - node selection via LSS fastscan
* - Inquire LSS address of currently selected node
* - Inquire node ID
* - Configure bit timing
* - Configure node ID
* - Activate bit timing parameters
* - Store configuration
*
* The LSS master is initalized during the CANopenNode initialization process.
* Except for enabling the LSS master in the configurator, no further
* run-time configuration is needed for basic operation.
* The LSS master does basic checking of commands and command sequence.
*
* ###Usage
*
* Usage of the CANopen LSS master is demonstrated in CANopenSocket application,
* see CO_LSS_master.c / CO_LSS_master.h files.
*
* It essentially is always as following:
* - select node(s)
* - call master command(s)
* - evaluate return value
* - deselect nodes
*
* All commands need to be run cyclically, e.g. like this
* \code{.c}
interval = 0;
do {
ret = CO_LSSmaster_InquireNodeId(LSSmaster, interval, &outval);
interval = 1; ms
sleep(interval);
} while (ret == CO_LSSmaster_WAIT_SLAVE);
* \endcode
*
* A more advanced implementation can make use of the callback function to
* shorten waiting times.
*/
/**
* Return values of LSS master functions.
*/
typedef enum {
CO_LSSmaster_SCAN_FINISHED = 2, /**< Scanning finished successful */
CO_LSSmaster_WAIT_SLAVE = 1, /**< No response arrived from server yet */
CO_LSSmaster_OK = 0, /**< Success, end of communication */
CO_LSSmaster_TIMEOUT = -1, /**< No reply received */
CO_LSSmaster_ILLEGAL_ARGUMENT = -2, /**< Invalid argument */
CO_LSSmaster_INVALID_STATE = -3, /**< State machine not ready or already processing a request */
CO_LSSmaster_SCAN_NOACK = -4, /**< No node found that matches scan request */
CO_LSSmaster_SCAN_FAILED = -5, /**< An error occurred while scanning. Try again */
CO_LSSmaster_OK_ILLEGAL_ARGUMENT = -101, /**< LSS success, node rejected argument because of non-supported value */
CO_LSSmaster_OK_MANUFACTURER = -102, /**< LSS success, node rejected argument with manufacturer error code */
} CO_LSSmaster_return_t;
/**
* LSS master object.
*/
typedef struct{
uint16_t timeout; /**< LSS response timeout in ms */
uint8_t state; /**< Node is currently selected */
uint8_t command; /**< Active command */
uint16_t timeoutTimer; /**< Timeout timer for LSS communication */
uint8_t fsState; /**< Current state of fastscan master state machine */
uint8_t fsLssSub; /**< Current state of node state machine */
uint8_t fsBitChecked; /**< Current scan bit position */
uint32_t fsIdNumber; /**< Current scan result */
volatile void *CANrxNew; /**< Indication if new LSS message is received from CAN bus. It needs to be cleared when received message is completely processed. */
uint8_t CANrxData[8]; /**< 8 data bytes of the received message */
void (*pFunctSignal)(void *object); /**< From CO_LSSmaster_initCallback() or NULL */
void *functSignalObject;/**< Pointer to object */
CO_CANmodule_t *CANdevTx; /**< From #CO_LSSslave_init() */
CO_CANtx_t *TXbuff; /**< CAN transmit buffer */
}CO_LSSmaster_t;
/**
* Default timeout for LSS slave in ms. This is the same as for SDO. For more
* info about LSS timeout see #CO_LSSmaster_changeTimeout()
*/
#define CO_LSSmaster_DEFAULT_TIMEOUT 1000U /* ms */
/**
* Initialize LSS object.
*
* Function must be called in the communication reset section.
*
* @param LSSmaster This object will be initialized.
* @param timeout_ms slave response timeout in ms, for more detail see
* #CO_LSSmaster_changeTimeout()
* @param CANdevRx CAN device for LSS master reception.
* @param CANdevRxIdx Index of receive buffer in the above CAN device.
* @param CANidLssSlave COB ID for reception.
* @param CANdevTx CAN device for LSS master transmission.
* @param CANdevTxIdx Index of transmit buffer in the above CAN device.
* @param CANidLssMaster COB ID for transmission.
* @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
*/
CO_ReturnError_t CO_LSSmaster_init(
CO_LSSmaster_t *LSSmaster,
uint16_t timeout_ms,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdx,
uint32_t CANidLssSlave,
CO_CANmodule_t *CANdevTx,
uint16_t CANdevTxIdx,
uint32_t CANidLssMaster);
/**
* Change LSS master timeout
*
* On LSS, a "negative ack" is signaled by the slave not answering. Because of
* that, a low timeout value can significantly increase protocol speed in some
* cases (e.g. fastscan). However, as soon as there is activity on the bus,
* LSS messages can be delayed because of their low CAN network priority (see
* #CO_Default_CAN_ID_t).
*
* @remark Be aware that a "late response" will seriously mess up LSS, so this
* value must be selected "as high as necessary and as low as possible". CiA does
* neither specify nor recommend a value.
*
* @remark This timeout is per-transfer. If a command internally needs multiple
* transfers to complete, this timeout is applied on each transfer.
*
* @param LSSmaster This object.
* @param timeout_ms timeout value in ms
*/
void CO_LSSmaster_changeTimeout(
CO_LSSmaster_t *LSSmaster,
uint16_t timeout_ms);
/**
* Initialize LSSserverRx callback function.
*
* Function initializes optional callback function, which is called after new
* message is received from the CAN bus. Function may wake up external task,
* which processes mainline CANopen functions.
*
* @param LSSmaster This object.
* @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL
* @param pFunctSignal Pointer to the callback function. Not called if NULL.
*/
void CO_LSSmaster_initCallback(
CO_LSSmaster_t *LSSmaster,
void *object,
void (*pFunctSignal)(void *object));
/**
* Request LSS switch state select
*
* This function can select one specific or all nodes.
*
* Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE
* Function is non-blocking.
*
* @remark Only one selection can be active at any time.
*
* @param LSSmaster This object.
* @param timeDifference_ms Time difference from previous function call in
* [milliseconds]. Zero when request is started.
* @param lssAddress LSS target address. If NULL, all nodes are selected
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE,
* #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, #CO_LSSmaster_TIMEOUT
*/
CO_LSSmaster_return_t CO_LSSmaster_switchStateSelect(
CO_LSSmaster_t *LSSmaster,
uint16_t timeDifference_ms,
CO_LSS_address_t *lssAddress);
/**
* Request LSS switch state deselect
*
* This function deselects all nodes, so it doesn't matter if a specific
* node is selected.
*
* This function also resets the LSS master state machine to a clean state
*
* @param LSSmaster This object.
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE,
* #CO_LSSmaster_OK
*/
CO_LSSmaster_return_t CO_LSSmaster_switchStateDeselect(
CO_LSSmaster_t *LSSmaster);
/**
* Request LSS configure Bit Timing
*
* The new bit rate is set as new pending value.
*
* This function needs one specific node to be selected.
*
* Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE.
* Function is non-blocking.
*
* @param LSSmaster This object.
* @param timeDifference_ms Time difference from previous function call in
* [milliseconds]. Zero when request is started.
* @param bit new bit rate
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE,
* #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, #CO_LSSmaster_TIMEOUT,
* #CO_LSSmaster_OK_MANUFACTURER, #CO_LSSmaster_OK_ILLEGAL_ARGUMENT
*/
CO_LSSmaster_return_t CO_LSSmaster_configureBitTiming(
CO_LSSmaster_t *LSSmaster,
uint16_t timeDifference_ms,
uint16_t bit);
/**
* Request LSS configure node ID
*
* The new node id is set as new pending node ID.
*
* This function needs one specific node to be selected.
*
* Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE.
* Function is non-blocking.
*
* @param LSSmaster This object.
* @param timeDifference_ms Time difference from previous function call in
* [milliseconds]. Zero when request is started.
* @param nodeId new node ID. Special value #CO_LSS_NODE_ID_ASSIGNMENT can be
* used to invalidate node ID.
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE,
* #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, #CO_LSSmaster_TIMEOUT,
* #CO_LSSmaster_OK_MANUFACTURER, #CO_LSSmaster_OK_ILLEGAL_ARGUMENT
*/
CO_LSSmaster_return_t CO_LSSmaster_configureNodeId(
CO_LSSmaster_t *LSSmaster,
uint16_t timeDifference_ms,
uint8_t nodeId);
/**
* Request LSS store configuration
*
* The current "pending" values for bit rate and node ID in LSS slave are
* stored as "permanent" values.
*
* This function needs one specific node to be selected.
*
* Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE.
* Function is non-blocking.
*
* @param LSSmaster This object.
* @param timeDifference_ms Time difference from previous function call in
* [milliseconds]. Zero when request is started.
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE,
* #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, #CO_LSSmaster_TIMEOUT,
* #CO_LSSmaster_OK_MANUFACTURER, #CO_LSSmaster_OK_ILLEGAL_ARGUMENT
*/
CO_LSSmaster_return_t CO_LSSmaster_configureStore(
CO_LSSmaster_t *LSSmaster,
uint16_t timeDifference_ms);
/**
* Request LSS activate bit timing
*
* The current "pending" bit rate in LSS slave is applied.
*
* Be aware that changing the bit rate is a critical step for the network. A
* failure will render the network unusable! Therefore, this function only
* should be called if the following conditions are met:
* - all nodes support changing bit timing
* - new bit timing is successfully set as "pending" in all nodes
* - all nodes have to activate the new bit timing roughly at the same time.
* Therefore this function needs all nodes to be selected.
*
* @param LSSmaster This object.
* @param switchDelay_ms delay that is applied by the slave once before and
* once after switching in ms.
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE,
* #CO_LSSmaster_OK
*/
CO_LSSmaster_return_t CO_LSSmaster_ActivateBit(
CO_LSSmaster_t *LSSmaster,
uint16_t switchDelay_ms);
/**
* Request LSS inquire LSS address
*
* The LSS address value is read from the node. This is useful when the node
* was selected by fastscan.
*
* This function needs one specific node to be selected.
*
* Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE.
* Function is non-blocking.
*
* @param LSSmaster This object.
* @param timeDifference_ms Time difference from previous function call in
* [milliseconds]. Zero when request is started.
* @param lssAddress [out] read result when function returns successfully
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE,
* #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, #CO_LSSmaster_TIMEOUT
*/
CO_LSSmaster_return_t CO_LSSmaster_InquireLssAddress(
CO_LSSmaster_t *LSSmaster,
uint16_t timeDifference_ms,
CO_LSS_address_t *lssAddress);
/**
* Request LSS inquire node ID
*
* The node ID value is read from the node.
*
* This function needs one specific node to be selected.
*
* Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE.
* Function is non-blocking.
*
* @param LSSmaster This object.
* @param timeDifference_ms Time difference from previous function call in
* [milliseconds]. Zero when request is started.
* @param nodeId [out] read result when function returns successfully
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE,
* #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, #CO_LSSmaster_TIMEOUT
*/
CO_LSSmaster_return_t CO_LSSmaster_InquireNodeId(
CO_LSSmaster_t *LSSmaster,
uint16_t timeDifference_ms,
uint8_t *nodeId);
/**
* Scan type for #CO_LSSmaster_fastscan_t scan
*/
typedef enum {
CO_LSSmaster_FS_SCAN = 0, /**< Do full 32 bit scan */
CO_LSSmaster_FS_SKIP = 1, /**< Skip this value */
CO_LSSmaster_FS_MATCH = 2, /**< Full 32 bit value is given as argument, just verify */
} CO_LSSmaster_scantype_t;
/**
* Parameters for LSS fastscan #CO_LSSmaster_IdentifyFastscan
*/
typedef struct{
CO_LSSmaster_scantype_t scan[4]; /**< Scan type for each part of the LSS address */
CO_LSS_address_t match; /**< Value to match in case of #CO_LSSmaster_FS_MATCH */
CO_LSS_address_t found; /**< Scan result */
} CO_LSSmaster_fastscan_t;
/**
* Select a node by LSS identify fastscan
*
* This initiates searching for a unconfigured node by the means of LSS fastscan
* mechanism. When this function is finished
* - a (more or less) arbitrary node is selected and ready for node ID assingment
* - no node is selected because the given criteria do not match a node
* - no node is selected because all nodes are already configured
*
* There are multiple ways to scan for a node. Depending on those, the scan
* will take different amounts of time:
* - full scan
* - partial scan
* - verification
*
* Most of the time, those are used in combination. Consider the following example:
* - Vendor ID and product code are known
* - Software version doesn't matter
* - Serial number is unknown
*
* In this case, the fastscan structure should be set up as following:
* \code{.c}
CO_LSSmaster_fastscan_t fastscan;
fastscan.scan[CO_LSS_FASTSCAN_VENDOR_ID] = CO_LSSmaster_FS_MATCH;
fastscan.match.vendorID = YOUR_VENDOR_ID;
fastscan.scan[CO_LSS_FASTSCAN_PRODUCT] = CO_LSSmaster_FS_MATCH;
fastscan.match.productCode = YOUR_PRODUCT_CODE;
fastscan.scan[CO_LSS_FASTSCAN_REV] = CO_LSSmaster_FS_SKIP;
fastscan.scan[CO_LSS_FASTSCAN_SERIAL] = CO_LSSmaster_FS_SCAN;
* \endcode
*
* This example will take 2 scan cyles for verifying vendor ID and product code
* and 33 scan cycles to find the serial number.
*
* For scanning, the following limitations apply:
* - No more than two values can be skipped
* - Vendor ID cannot be skipped
*
* @remark When doing partial scans, it is in the responsibility of the user
* that the LSS address is unique.
*
* This function needs that no node is selected when starting the scan process.
*
* Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE.
* Function is non-blocking.
*
* @param LSSmaster This object.
* @param timeDifference_ms Time difference from previous function call in
* [milliseconds]. Zero when request is started.
* @param fastscan struct according to #CO_LSSmaster_fastscan_t.
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE,
* #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_SCAN_FINISHED, #CO_LSSmaster_SCAN_NOACK,
* #CO_LSSmaster_SCAN_FAILED
*/
CO_LSSmaster_return_t CO_LSSmaster_IdentifyFastscan(
CO_LSSmaster_t *LSSmaster,
uint16_t timeDifference_ms,
CO_LSSmaster_fastscan_t *fastscan);
#else /* CO_NO_LSS_CLIENT == 1 */
/**
* @addtogroup CO_LSS
* @{
* If you need documetation for LSS master usage, add "CO_NO_LSS_CLIENT=1" to doxygen
* "PREDEFINED" variable.
*
*/
#endif /* CO_NO_LSS_CLIENT == 1 */
#ifdef __cplusplus
}
#endif /*__cplusplus*/
/** @} */
#endif

View File

@ -0,0 +1,529 @@
/*
* CANopen LSS Slave protocol.
*
* @file CO_LSSslave.c
* @ingroup CO_LSS
* @author Martin Wagner
* @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH
*
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "CANopen.h"
#include "CO_LSSslave.h"
#if CO_NO_LSS_SERVER == 1
/*
* Helper function - Handle service "switch state global"
*/
static void CO_LSSslave_serviceSwitchStateGlobal(
CO_LSSslave_t *LSSslave,
CO_LSS_cs_t service,
const CO_CANrxMsg_t *msg)
{
uint8_t mode = msg->data[1];
switch (mode) {
case CO_LSS_STATE_WAITING:
LSSslave->lssState = CO_LSS_STATE_WAITING;
CO_memset((uint8_t*)&LSSslave->lssSelect, 0, sizeof(LSSslave->lssSelect));
break;
case CO_LSS_STATE_CONFIGURATION:
LSSslave->lssState = CO_LSS_STATE_CONFIGURATION;
break;
default:
break;
}
}
/*
* Helper function - Handle service "switch state selective"
*/
static void CO_LSSslave_serviceSwitchStateSelective(
CO_LSSslave_t *LSSslave,
CO_LSS_cs_t service,
const CO_CANrxMsg_t *msg)
{
uint32_t value;
CO_memcpySwap4(&value, &msg->data[1]);
if(LSSslave->lssState != CO_LSS_STATE_WAITING) {
return;
}
switch (service) {
case CO_LSS_SWITCH_STATE_SEL_VENDOR:
LSSslave->lssSelect.identity.vendorID = value;
break;
case CO_LSS_SWITCH_STATE_SEL_PRODUCT:
LSSslave->lssSelect.identity.productCode = value;
break;
case CO_LSS_SWITCH_STATE_SEL_REV:
LSSslave->lssSelect.identity.revisionNumber = value;
break;
case CO_LSS_SWITCH_STATE_SEL_SERIAL:
LSSslave->lssSelect.identity.serialNumber = value;
if (CO_LSS_ADDRESS_EQUAL(LSSslave->lssAddress, LSSslave->lssSelect)) {
LSSslave->lssState = CO_LSS_STATE_CONFIGURATION;
/* send confirmation */
LSSslave->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL;
CO_memset(&LSSslave->TXbuff->data[1], 0, 7);
CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff);
}
break;
default:
break;
}
}
/*
* Helper function - Handle service "configure"
*
* values inside message have different meaning, depending on the selected
* configuration type
*/
static void CO_LSSslave_serviceConfig(
CO_LSSslave_t *LSSslave,
CO_LSS_cs_t service,
const CO_CANrxMsg_t *msg)
{
uint8_t nid;
uint8_t tableSelector;
uint8_t tableIndex;
uint8_t errorCode;
if(LSSslave->lssState != CO_LSS_STATE_CONFIGURATION) {
return;
}
switch (service) {
case CO_LSS_CFG_NODE_ID:
nid = msg->data[1];
errorCode = CO_LSS_CFG_NODE_ID_OK;
if (CO_LSS_NODE_ID_VALID(nid)) {
LSSslave->pendingNodeID = nid;
}
else {
errorCode = CO_LSS_CFG_NODE_ID_OUT_OF_RANGE;
}
/* send confirmation */
LSSslave->TXbuff->data[0] = CO_LSS_CFG_NODE_ID;
LSSslave->TXbuff->data[1] = errorCode;
/* we do not use spec-error, always 0 */
CO_memset(&LSSslave->TXbuff->data[2], 0, 6);
CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff);
break;
case CO_LSS_CFG_BIT_TIMING:
if (LSSslave->pFunctLSScheckBitRate == NULL) {
/* setting bit timing is not supported. Drop request */
break;
}
tableSelector = msg->data[1];
tableIndex = msg->data[2];
errorCode = CO_LSS_CFG_BIT_TIMING_OK;
if (tableSelector==0 && CO_LSS_BIT_TIMING_VALID(tableIndex)) {
uint16_t bit = CO_LSS_bitTimingTableLookup[tableIndex];
bool_t bit_rate_supported = LSSslave->pFunctLSScheckBitRate(
LSSslave->functLSScheckBitRateObject, bit);
if (bit_rate_supported) {
LSSslave->pendingBitRate = bit;
}
else {
errorCode = CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE;
}
}
else {
/* we currently only support CiA301 bit timing table */
errorCode = CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE;
}
/* send confirmation */
LSSslave->TXbuff->data[0] = CO_LSS_CFG_BIT_TIMING;
LSSslave->TXbuff->data[1] = errorCode;
/* we do not use spec-error, always 0 */
CO_memset(&LSSslave->TXbuff->data[2], 0, 6);
CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff);
break;
case CO_LSS_CFG_ACTIVATE_BIT_TIMING:
if (LSSslave->pFunctLSScheckBitRate == NULL) {
/* setting bit timing is not supported. Drop request */
break;
}
/* notify application */
if (LSSslave->pFunctLSSactivateBitRate != NULL) {
uint16_t delay;
CO_memcpySwap2(&delay, &msg->data[1]);
LSSslave->pFunctLSSactivateBitRate(
LSSslave->functLSSactivateBitRateObject, delay);
}
break;
case CO_LSS_CFG_STORE:
errorCode = CO_LSS_CFG_STORE_OK;
if (LSSslave->pFunctLSScfgStore == NULL) {
/* storing is not supported. Reply error */
errorCode = CO_LSS_CFG_STORE_NOT_SUPPORTED;
}
else {
bool_t result;
/* Store "pending" to "persistent" */
result = LSSslave->pFunctLSScfgStore(LSSslave->functLSScfgStore,
LSSslave->pendingNodeID, LSSslave->pendingBitRate);
if (!result) {
errorCode = CO_LSS_CFG_STORE_FAILED;
}
}
/* send confirmation */
LSSslave->TXbuff->data[0] = CO_LSS_CFG_STORE;
LSSslave->TXbuff->data[1] = errorCode;
/* we do not use spec-error, always 0 */
CO_memset(&LSSslave->TXbuff->data[2], 0, 6);
CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff);
break;
default:
break;
}
}
/*
* Helper function - Handle service "inquire"
*/
static void CO_LSSslave_serviceInquire(
CO_LSSslave_t *LSSslave,
CO_LSS_cs_t service,
const CO_CANrxMsg_t *msg)
{
uint32_t value;
if(LSSslave->lssState != CO_LSS_STATE_CONFIGURATION) {
return;
}
switch (service) {
case CO_LSS_INQUIRE_VENDOR:
value = LSSslave->lssAddress.identity.vendorID;
break;
case CO_LSS_INQUIRE_PRODUCT:
value = LSSslave->lssAddress.identity.productCode;
break;
case CO_LSS_INQUIRE_REV:
value = LSSslave->lssAddress.identity.revisionNumber;
break;
case CO_LSS_INQUIRE_SERIAL:
value = LSSslave->lssAddress.identity.serialNumber;
break;
case CO_LSS_INQUIRE_NODE_ID:
value = (uint32_t)LSSslave->activeNodeID;
break;
default:
return;
}
/* send response */
LSSslave->TXbuff->data[0] = service;
CO_memcpySwap4(&LSSslave->TXbuff->data[1], &value);
CO_memset(&LSSslave->TXbuff->data[5], 0, 4);
CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff);
}
/*
* Helper function - Handle service "identify"
*/
static void CO_LSSslave_serviceIdent(
CO_LSSslave_t *LSSslave,
CO_LSS_cs_t service,
const CO_CANrxMsg_t *msg)
{
uint32_t idNumber;
uint8_t bitCheck;
uint8_t lssSub;
uint8_t lssNext;
bool_t ack;
if (LSSslave->lssState != CO_LSS_STATE_WAITING) {
/* fastscan is only allowed in waiting state */
return;
}
if (service != CO_LSS_IDENT_FASTSCAN) {
/* we only support "fastscan" identification */
return;
}
if (LSSslave->pendingNodeID!=CO_LSS_NODE_ID_ASSIGNMENT ||
LSSslave->activeNodeID!=CO_LSS_NODE_ID_ASSIGNMENT) {
/* fastscan is only active on unconfigured nodes */
return;
}
CO_memcpySwap4(&idNumber, &msg->data[1]);
bitCheck = msg->data[5];
lssSub = msg->data[6];
lssNext = msg->data[7];
if (!CO_LSS_FASTSCAN_BITCHECK_VALID(bitCheck) ||
!CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(lssSub) ||
!CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(lssNext)) {
/* Invalid request */
return;
}
ack = false;
if (bitCheck == CO_LSS_FASTSCAN_CONFIRM) {
/* Confirm, Reset */
ack = true;
LSSslave->fastscanPos = CO_LSS_FASTSCAN_VENDOR_ID;
CO_memset((uint8_t*)&LSSslave->lssFastscan, 0,
sizeof(LSSslave->lssFastscan));
}
else if (LSSslave->fastscanPos == lssSub) {
uint32_t mask = 0xFFFFFFFF << bitCheck;
if ((LSSslave->lssAddress.addr[lssSub] & mask) == (idNumber & mask)) {
/* all requested bits match */
ack = true;
LSSslave->fastscanPos = lssNext;
if (bitCheck==0 && lssNext<lssSub) {
/* complete match, enter configuration state */
LSSslave->lssState = CO_LSS_STATE_CONFIGURATION;
}
}
}
if (ack) {
LSSslave->TXbuff->data[0] = CO_LSS_IDENT_SLAVE;
CO_memset(&LSSslave->TXbuff->data[1], 0, 7);
CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff);
}
}
/*
* Read received message from CAN module.
*
* Function will be called (by CAN receive interrupt) every time, when CAN
* message with correct identifier will be received. For more information and
* description of parameters see file CO_driver.h.
*/
static void CO_LSSslave_receive(void *object, const CO_CANrxMsg_t *msg)
{
CO_LSSslave_t *LSSslave;
LSSslave = (CO_LSSslave_t*)object; /* this is the correct pointer type of the first argument */
if(msg->DLC == 8){
CO_LSS_cs_t cs = msg->data[0];
if (CO_LSS_CS_SERVICE_IS_SWITCH_GLOBAL(cs)) {
CO_LSSslave_serviceSwitchStateGlobal(LSSslave, cs, msg);
}
else if (CO_LSS_CS_SERVICE_IS_SWITCH_STATE_SELECTIVE(cs)) {
CO_LSSslave_serviceSwitchStateSelective(LSSslave, cs, msg);
}
else if (CO_LSS_CS_SERVICE_IS_CONFIG(cs)) {
CO_LSSslave_serviceConfig(LSSslave, cs, msg);
}
else if (CO_LSS_CS_SERVICE_IS_INQUIRE(cs)) {
CO_LSSslave_serviceInquire(LSSslave, cs, msg);
}
else if (CO_LSS_CS_SERVICE_IS_IDENT(cs)) {
CO_LSSslave_serviceIdent(LSSslave, cs, msg);
}
else {
/* No Ack -> Unsupported commands are dropped */
}
}
}
/******************************************************************************/
CO_ReturnError_t CO_LSSslave_init(
CO_LSSslave_t *LSSslave,
CO_LSS_address_t lssAddress,
uint16_t pendingBitRate,
uint8_t pendingNodeID,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdx,
uint32_t CANidLssMaster,
CO_CANmodule_t *CANdevTx,
uint16_t CANdevTxIdx,
uint32_t CANidLssSlave)
{
/* verify arguments */
if (LSSslave==NULL || CANdevRx==NULL || CANdevTx==NULL ||
!CO_LSS_NODE_ID_VALID(pendingNodeID)) {
return CO_ERROR_ILLEGAL_ARGUMENT;
}
/* check LSS address for plausibility. As a bare minimum, the vendor
* ID and serial number must be set */
if (lssAddress.identity.vendorID==0 || lssAddress.identity.serialNumber==0) {
return CO_ERROR_ILLEGAL_ARGUMENT;
}
CO_memcpy((uint8_t*)&LSSslave->lssAddress, (uint8_t*)&lssAddress, sizeof(LSSslave->lssAddress));
LSSslave->lssState = CO_LSS_STATE_WAITING;
CO_memset((uint8_t*)&LSSslave->lssSelect, 0, sizeof(LSSslave->lssSelect));
CO_memset((uint8_t*)&LSSslave->lssFastscan, 0, sizeof(LSSslave->lssFastscan));
LSSslave->fastscanPos = CO_LSS_FASTSCAN_VENDOR_ID;
LSSslave->pendingBitRate = pendingBitRate;
LSSslave->pendingNodeID = pendingNodeID;
LSSslave->activeNodeID = CO_LSS_NODE_ID_ASSIGNMENT;
LSSslave->pFunctLSScheckBitRate = NULL;
LSSslave->functLSScheckBitRateObject = NULL;
LSSslave->pFunctLSSactivateBitRate = NULL;
LSSslave->functLSSactivateBitRateObject = NULL;
LSSslave->pFunctLSScfgStore = NULL;
LSSslave->functLSScfgStore = NULL;
/* configure LSS CAN Master message reception */
CO_CANrxBufferInit(
CANdevRx, /* CAN device */
CANdevRxIdx, /* rx buffer index */
CANidLssMaster, /* CAN identifier */
0x7FF, /* mask */
0, /* rtr */
(void*)LSSslave, /* object passed to receive function */
CO_LSSslave_receive); /* this function will process received message */
/* configure LSS CAN Slave response message transmission */
LSSslave->CANdevTx = CANdevTx;
LSSslave->TXbuff = CO_CANtxBufferInit(
CANdevTx, /* CAN device */
CANdevTxIdx, /* index of specific buffer inside CAN module */
CANidLssSlave, /* CAN identifier */
0, /* rtr */
8, /* number of data bytes */
0); /* synchronous message flag bit */
return CO_ERROR_NO;
}
/******************************************************************************/
void CO_LSSslave_initCheckBitRateCallback(
CO_LSSslave_t *LSSslave,
void *object,
bool_t (*pFunctLSScheckBitRate)(void *object, uint16_t bitRate))
{
if(LSSslave != NULL){
LSSslave->functLSScheckBitRateObject = object;
LSSslave->pFunctLSScheckBitRate = pFunctLSScheckBitRate;
}
}
/******************************************************************************/
void CO_LSSslave_initActivateBitRateCallback(
CO_LSSslave_t *LSSslave,
void *object,
void (*pFunctLSSactivateBitRate)(void *object, uint16_t delay))
{
if(LSSslave != NULL){
LSSslave->functLSSactivateBitRateObject = object;
LSSslave->pFunctLSSactivateBitRate = pFunctLSSactivateBitRate;
}
}
/******************************************************************************/
void CO_LSSslave_initCfgStoreCallback(
CO_LSSslave_t *LSSslave,
void *object,
bool_t (*pFunctLSScfgStore)(void *object, uint8_t id, uint16_t bitRate))
{
if(LSSslave != NULL){
LSSslave->functLSScfgStore = object;
LSSslave->pFunctLSScfgStore = pFunctLSScfgStore;
}
}
/******************************************************************************/
void CO_LSSslave_process(
CO_LSSslave_t *LSSslave,
uint16_t activeBitRate,
uint8_t activeNodeId,
uint16_t *pendingBitRate,
uint8_t *pendingNodeId)
{
LSSslave->activeNodeID = activeNodeId;
*pendingBitRate = LSSslave->pendingBitRate;
*pendingNodeId = LSSslave->pendingNodeID;
}
/******************************************************************************/
CO_LSS_state_t CO_LSSslave_getState(
CO_LSSslave_t *LSSslave)
{
if(LSSslave != NULL){
return LSSslave->lssState;
}
return CO_LSS_STATE_WAITING;
}
/******************************************************************************/
bool_t CO_LSSslave_LEDprocess(
CO_LSSslave_t *LSSslave,
uint16_t timeDifference_ms,
bool_t *LEDon)
{
static uint16_t ms50 = 0;
static int8_t flash1, flash2;
if (LSSslave == NULL || LEDon == NULL)
return false;
ms50 += timeDifference_ms;
if(ms50 >= 50) {
ms50 -= 50;
/* 4 cycles on, 50 cycles off */
if(++flash1 >= 4) flash1 = -50;
/* 4 cycles on, 4 cycles off, 4 cycles on, 50 cycles off */
switch(++flash2){
case 4: flash2 = -104; break;
case -100: flash2 = 100; break;
case 104: flash2 = -50; break;
}
}
if (LSSslave->lssState == CO_LSS_STATE_CONFIGURATION)
{
*LEDon = (flash2 >= 0);
return true;
}
else if (LSSslave->activeNodeID == CO_LSS_NODE_ID_ASSIGNMENT)
{
*LEDon = (flash1 >= 0);
return true;
}
return false;
}
#endif

View File

@ -0,0 +1,430 @@
/**
* CANopen LSS Master/Slave protocol.
*
* @file CO_LSSslave.h
* @ingroup CO_LSS
* @author Martin Wagner
* @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH
*
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CO_LSSslave_H
#define CO_LSSslave_H
#ifdef __cplusplus
extern "C" {
#endif
#if CO_NO_LSS_SERVER == 1
#include "CO_LSS.h"
/**
* @addtogroup CO_LSS
* @defgroup CO_LSSslave LSS Slave
* @ingroup CO_LSS
* @{
*
* CANopen Layer Setting Service - server protocol
*
* The server/slave provides the following services
* - node selection via LSS address
* - node selection via LSS fastscan
* - Inquire LSS address of currently selected node
* - Inquire node ID
* - Configure bit timing
* - Configure node ID
* - Activate bit timing parameters
* - Store configuration (bit rate and node ID)
*
* After CAN module start, the LSS server and NMT server are started and then
* coexist alongside each other. To achieve this behaviour, the CANopen node
* startup process has to be conrolled more detailled. Therefore, the function
* CO_init() is split up into the functions #CO_new(), #CO_CANinit(), #CO_LSSinit()
* and #CO_CANopenInit().
* Moreover, the LSS server needs to pause the NMT server initialization in case
* no valid node ID is available at start up.
*
* ###Example
*
* It is strongly recommended that the user already has a fully working application
* running with the standard (non LSS) version of CANopenNode. This is required
* to understand what this example does and where you need to change it for your
* requirements.
*
* The following code is only a suggestion on how to use the LSS server. It is
* not a working example! To simplify the code, no error handling is
* included. For stable code, proper error handling has to be added to the user
* code.
*
* This example is not intended for bare metal targets. If you intend to do CAN
* message receiving inside interrupt, be aware that the callback functions
* will be called inside the interrupt handler context!
*
* \code{.c}
const uint16_t FIRST_BIT = 125;
queue changeBitRate;
uint8_t activeNid;
uint16_t activeBit;
bool_t checkBitRateCallback(void *object, uint16_t bitRate)
{
if (validBit(bitRate)) {
return true;
}
return false;
}
void activateBitRateCallback(void *object, uint16_t delay)
{
int time = getCurrentTime();
queueSend(&changeBitRate, time, delay);
}
bool_t cfgStoreCallback(void *object, uint8_t id, uint16_t bitRate)
{
savePersistent(id, bitRate);
return true;
}
void start_canopen(uint8_t nid)
{
uint8_t persistentNid;
uint8_t pendingNid;
uint16_t persistentBit;
uint16_t pendingBit;
loadPersistent(&persistentNid, &persistentBit);
if ( ! validBit(persistentBit)) {
printf("no bit rate found, defaulting to %d", FIRST_BIT);
pendingBit = FIRST_BIT;
}
else {
printf("loaded bit rate from nvm: %d", persistentBit);
pendingBit = persistentBit;
}
if (nid == 0) {
if ( ! validNid(persistentNid)) {
pendingNid = CO_LSS_NODE_ID_ASSIGNMENT;
printf("no node id found, needs to be set by LSS. NMT will"
"not be started until valid node id is set");
}
else {
printf("loaded node id from nvm: %d", persistentNid);
pendingNid = persistentNid;
}
}
else {
printf("node id provided by application: %d", nid);
pendingNid = nid;
}
CO_new();
CO_CANinit(0, pendingBit);
CO_LSSinit(pendingNid, pendingBit);
CO_CANsetNormalMode(CO->CANmodule[0]);
activeBit = pendingBit;
CO_LSSslave_initCheckBitRateCallback(CO->LSSslave, NULL, checkBitRateCallback);
CO_LSSslave_initActivateBitRateCallback(CO->LSSslave, NULL, activateBitRateCallback);
CO_LSSslave_initCfgStoreCallback(CO->LSSslave, NULL, cfgStoreCallback);
while (1) {
CO_LSSslave_process(CO->LSSslave, activeBit, activeNid,
&pendingBit, &pendingNid);
if (pendingNid!=CO_LSS_NODE_ID_ASSIGNMENT &&
CO_LSSslave_getState(CO->LSSslave)==CO_LSS_STATE_WAITING) {
printf("node ID has been found: %d", pendingNid);
break;
}
if ( ! queueEmpty(&changeBitRate)) {
printf("bit rate change requested: %d", pendingBit);
int time;
uint16_t delay;
queueReceive(&changeBitRate, time, delay);
delayUntil(time + delay);
CO_CANsetBitrate(CO->CANmodule[0], pendingBit);
delay(delay);
}
printf("waiting for node id");
CO_CANrxWait(CO->CANmodule[0]);
}
CO_CANopenInit(pendingNid);
activeNid = pendingNid;
printf("from this on, initialization doesn't differ to non-LSS version"
"You can now intialize your CO_CANrxWait() thread or interrupt");
}
void main(void)
{
uint8_t pendingNid;
uint16_t pendingBit;
printf("like example in dir \"example\"");
CO_NMT_reset_cmd_t reset = CO_RESET_NOT;
uint16_t timer1msPrevious;
start_canopen(0);
reset = CO_RESET_NOT;
timer1msPrevious = CO_timer1ms;
while(reset == CO_RESET_NOT){
printf("loop for normal program execution");
uint16_t timer1msCopy, timer1msDiff;
timer1msCopy = CO_timer1ms;
timer1msDiff = timer1msCopy - timer1msPrevious;
timer1msPrevious = timer1msCopy;
reset = CO_process(CO, timer1msDiff, NULL);
CO_LSSslave_process(CO->LSSslave, activeBit, activeNid,
&pendingBit, &pendingNid);
if (reset == CO_RESET_COMM) {
printf("restarting CANopen using pending node ID %d", pendingNid);
CO_delete(0);
start_canopen(pendingNid);
reset = CO_RESET_NOT;
}
if ( ! queueEmpty(&changeBitRate)) {
printf("bit rate change requested: %d", pendingBit);
int time;
uint16_t delay;
queueReceive(&changeBitRate, time, delay);
printf("Disabling CANopen for givent time");
pauseReceiveThread();
delayUntil(time + delay);
CO_CANsetBitrate(CO->CANmodule[0], pendingBit);
delay(delay);
resumeReceiveThread();
printf("Re-enabling CANopen after bit rate switch");
}
}
}
* \endcode
*/
/**
* LSS slave object.
*/
typedef struct{
CO_LSS_address_t lssAddress; /**< From #CO_LSSslave_init */
CO_LSS_state_t lssState; /**< #CO_LSS_state_t */
CO_LSS_address_t lssSelect; /**< Received LSS Address by select */
CO_LSS_address_t lssFastscan; /**< Received LSS Address by fastscan */
uint8_t fastscanPos; /**< Current state of fastscan */
uint16_t pendingBitRate; /**< Bit rate value that is temporarily configured in volatile memory */
uint8_t pendingNodeID; /**< Node ID that is temporarily configured in volatile memory */
uint8_t activeNodeID; /**< Node ID used at the CAN interface */
bool_t (*pFunctLSScheckBitRate)(void *object, uint16_t bitRate); /**< From CO_LSSslave_initCheckBitRateCallback() or NULL */
void *functLSScheckBitRateObject; /** Pointer to object */
void (*pFunctLSSactivateBitRate)(void *object, uint16_t delay); /**< From CO_LSSslave_initActivateBitRateCallback() or NULL. Delay is in ms */
void *functLSSactivateBitRateObject; /** Pointer to object */
bool_t (*pFunctLSScfgStore)(void *object, uint8_t id, uint16_t bitRate); /**< From CO_LSSslave_initCfgStoreCallback() or NULL */
void *functLSScfgStore; /** Pointer to object */
CO_CANmodule_t *CANdevTx; /**< From #CO_LSSslave_init() */
CO_CANtx_t *TXbuff; /**< CAN transmit buffer */
}CO_LSSslave_t;
/**
* Initialize LSS object.
*
* Function must be called in the communication reset section.
*
* Depending on the startup type, pending bit rate and node ID have to be
* supplied differently. After #CO_NMT_RESET_NODE or at power up they should
* be restored from persitent bit rate and node id. After #CO_NMT_RESET_COMMUNICATION
* they have to be supplied from the application and are generally the values
* that have been last returned by #CO_LSSslave_process() before resetting.
*
* @remark The LSS address needs to be unique on the network. For this, the 128
* bit wide identity object (1018h) is used. Therefore, this object has to be fully
* initalized before passing it to this function.
*
* @param LSSslave This object will be initialized.
* @param lssAddress LSS address
* @param pendingBitRate Bit rate of the CAN interface.
* @param pendingNodeID Node ID or 0xFF - invalid.
* @param CANdevRx CAN device for LSS slave reception.
* @param CANdevRxIdx Index of receive buffer in the above CAN device.
* @param CANidLssMaster COB ID for reception.
* @param CANdevTx CAN device for LSS slave transmission.
* @param CANdevTxIdx Index of transmit buffer in the above CAN device.
* @param CANidLssSlave COB ID for transmission.
* @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
*/
CO_ReturnError_t CO_LSSslave_init(
CO_LSSslave_t *LSSslave,
CO_LSS_address_t lssAddress,
uint16_t pendingBitRate,
uint8_t pendingNodeID,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdx,
uint32_t CANidLssMaster,
CO_CANmodule_t *CANdevTx,
uint16_t CANdevTxIdx,
uint32_t CANidLssSlave);
/**
* Process LSS communication
*
* - sets currently active node ID and bit rate so master can read it
* - hands over pending node ID and bit rate to user application
*
* @param LSSslave This object.
* @param activeBitRate Currently active bit rate
* @param activeNodeId Currently active node ID
* @param pendingBitRate [out] Requested bit rate
* @param pendingNodeId [out] Requested node id
*/
void CO_LSSslave_process(
CO_LSSslave_t *LSSslave,
uint16_t activeBitRate,
uint8_t activeNodeId,
uint16_t *pendingBitRate,
uint8_t *pendingNodeId);
/**
* Get current LSS state
*
* @param LSSslave This object.
* @return #CO_LSS_state_t
*/
CO_LSS_state_t CO_LSSslave_getState(
CO_LSSslave_t *LSSslave);
/**
* Process LSS LED
*
* Returns the status of the LSS LED (if LSS is involved)
* with the following meaning:
*
* UNCONFIGURED (activeNodeId is unconfigured) --> single flash
* SELECTED --> double flash
*
* If none of above conditions apply, returns false.
*
* @param LSSslave This object.
* @param timeDifference_ms The amount of time elapsed since the last call
* @param LEDon [out] LED state
*
* @return true if LSS is involved (unconfigured node or selected node)
*/
bool_t CO_LSSslave_LEDprocess(
CO_LSSslave_t *LSSslave,
uint16_t timeDifference_ms,
bool_t *LEDon);
/**
* Initialize verify bit rate callback
*
* Function initializes callback function, which is called when "config bit
* timing parameters" is used. The callback function needs to check if the new bit
* rate is supported by the CANopen device. Callback returns "true" if supported.
* When no callback is set the LSS server will no-ack the request, indicating to
* the master that bit rate change is not supported.
*
* @remark Depending on the CAN driver implementation, this function is called
* inside an ISR
*
* @param LSSslave This object.
* @param object Pointer to object, which will be passed to pFunctLSScheckBitRate(). Can be NULL
* @param pFunctLSScheckBitRate Pointer to the callback function. Not called if NULL.
*/
void CO_LSSslave_initCheckBitRateCallback(
CO_LSSslave_t *LSSslave,
void *object,
bool_t (*pFunctLSScheckBitRate)(void *object, uint16_t bitRate));
/**
* Initialize activate bit rate callback
*
* Function initializes callback function, which is called when "activate bit
* timing parameters" is used. The callback function gives the user an event to
* allow setting a timer or do calculations based on the exact time the request
* arrived.
* According to DSP 305 6.4.4, the delay has to be applied once before and once after
* switching bit rates. During this time, a device musn't send any messages.
*
* @remark Depending on the CAN driver implementation, this function is called
* inside an ISR
*
* @param LSSslave This object.
* @param object Pointer to object, which will be passed to pFunctLSSactivateBitRate(). Can be NULL
* @param pFunctLSSactivateBitRate Pointer to the callback function. Not called if NULL.
*/
void CO_LSSslave_initActivateBitRateCallback(
CO_LSSslave_t *LSSslave,
void *object,
void (*pFunctLSSactivateBitRate)(void *object, uint16_t delay));
/**
* Store configuration callback
*
* Function initializes callback function, which is called when "store configuration" is used.
* The callback function gives the user an event to store the corresponding node id and bit rate
* to NVM. Those values have to be supplied to the init function as "persistent values"
* after reset. If callback returns "true", success is send to the LSS master. When no
* callback is set the LSS server will no-ack the request, indicating to the master
* that storing is not supported.
*
* @remark Depending on the CAN driver implementation, this function is called
* inside an ISR
*
* @param LSSslave This object.
* @param object Pointer to object, which will be passed to pFunctLSScfgStore(). Can be NULL
* @param pFunctLSScfgStore Pointer to the callback function. Not called if NULL.
*/
void CO_LSSslave_initCfgStoreCallback(
CO_LSSslave_t *LSSslave,
void *object,
bool_t (*pFunctLSScfgStore)(void *object, uint8_t id, uint16_t bitRate));
#else /* CO_NO_LSS_SERVER == 1 */
/**
* @addtogroup CO_LSS
* @{
* If you need documetation for LSS slave usage, add "CO_NO_LSS_SERVER=1" to doxygen
* "PREDEFINED" variable.
*
*/
#endif /* CO_NO_LSS_SERVER == 1 */
#ifdef __cplusplus
}
#endif /*__cplusplus*/
/** @} */
#endif

View File

@ -0,0 +1,353 @@
/*
* CANopen NMT and Heartbeat producer object.
*
* @file CO_NMT_Heartbeat.c
* @ingroup CO_NMT_Heartbeat
* @author Janez Paternoster
* @copyright 2004 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "CO_driver.h"
#include "CO_SDO.h"
#include "CO_Emergency.h"
#include "CO_NMT_Heartbeat.h"
/*
* Read received message from CAN module.
*
* Function will be called (by CAN receive interrupt) every time, when CAN
* message with correct identifier will be received. For more information and
* description of parameters see file CO_driver.h.
*/
static void CO_NMT_receive(void *object, const CO_CANrxMsg_t *msg){
CO_NMT_t *NMT;
uint8_t nodeId;
NMT = (CO_NMT_t*)object; /* this is the correct pointer type of the first argument */
nodeId = msg->data[1];
if((msg->DLC == 2) && ((nodeId == 0) || (nodeId == NMT->nodeId))){
uint8_t command = msg->data[0];
uint8_t currentOperatingState = NMT->operatingState;
switch(command){
case CO_NMT_ENTER_OPERATIONAL:
if((*NMT->emPr->errorRegister) == 0U){
NMT->operatingState = CO_NMT_OPERATIONAL;
}
break;
case CO_NMT_ENTER_STOPPED:
NMT->operatingState = CO_NMT_STOPPED;
break;
case CO_NMT_ENTER_PRE_OPERATIONAL:
NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
break;
case CO_NMT_RESET_NODE:
NMT->resetCommand = CO_RESET_APP;
break;
case CO_NMT_RESET_COMMUNICATION:
NMT->resetCommand = CO_RESET_COMM;
break;
default:
break;
}
if(NMT->pFunctNMT!=NULL && currentOperatingState!=NMT->operatingState){
NMT->pFunctNMT(NMT->operatingState);
}
}
}
/******************************************************************************/
CO_ReturnError_t CO_NMT_init(
CO_NMT_t *NMT,
CO_EMpr_t *emPr,
uint8_t nodeId,
uint16_t firstHBTime,
CO_CANmodule_t *NMT_CANdev,
uint16_t NMT_rxIdx,
uint16_t CANidRxNMT,
CO_CANmodule_t *HB_CANdev,
uint16_t HB_txIdx,
uint16_t CANidTxHB)
{
/* verify arguments */
if(NMT==NULL || emPr==NULL || NMT_CANdev==NULL || HB_CANdev==NULL){
return CO_ERROR_ILLEGAL_ARGUMENT;
}
/* blinking bytes */
#ifdef CO_USE_LEDS
NMT->LEDflickering = 0;
NMT->LEDblinking = 0;
NMT->LEDsingleFlash = 0;
NMT->LEDdoubleFlash = 0;
NMT->LEDtripleFlash = 0;
NMT->LEDquadrupleFlash = 0;
#endif /* CO_USE_LEDS */
/* Configure object variables */
NMT->operatingState = CO_NMT_INITIALIZING;
#ifdef CO_USE_LEDS
NMT->LEDgreenRun = -1;
NMT->LEDredError = 1;
#endif /* CO_USE_LEDS */
NMT->nodeId = nodeId;
NMT->firstHBTime = firstHBTime;
NMT->resetCommand = 0;
NMT->HBproducerTimer = 0xFFFF;
NMT->emPr = emPr;
NMT->pFunctNMT = NULL;
/* configure NMT CAN reception */
CO_CANrxBufferInit(
NMT_CANdev, /* CAN device */
NMT_rxIdx, /* rx buffer index */
CANidRxNMT, /* CAN identifier */
0x7FF, /* mask */
0, /* rtr */
(void*)NMT, /* object passed to receive function */
CO_NMT_receive); /* this function will process received message */
/* configure HB CAN transmission */
NMT->HB_CANdev = HB_CANdev;
NMT->HB_TXbuff = CO_CANtxBufferInit(
HB_CANdev, /* CAN device */
HB_txIdx, /* index of specific buffer inside CAN module */
CANidTxHB, /* CAN identifier */
0, /* rtr */
1, /* number of data bytes */
0); /* synchronous message flag bit */
return CO_ERROR_NO;
}
/******************************************************************************/
void CO_NMT_initCallback(
CO_NMT_t *NMT,
void (*pFunctNMT)(CO_NMT_internalState_t state))
{
if(NMT != NULL){
NMT->pFunctNMT = pFunctNMT;
if(NMT->pFunctNMT != NULL){
NMT->pFunctNMT(NMT->operatingState);
}
}
}
/******************************************************************************/
#ifdef CO_USE_LEDS
void CO_NMT_blinkingProcess50ms(CO_NMT_t *NMT){
if(++NMT->LEDflickering >= 1) NMT->LEDflickering = -1;
if(++NMT->LEDblinking >= 4) NMT->LEDblinking = -4;
if(++NMT->LEDsingleFlash >= 4) NMT->LEDsingleFlash = -20;
switch(++NMT->LEDdoubleFlash){
case 4: NMT->LEDdoubleFlash = -104; break;
case -100: NMT->LEDdoubleFlash = 100; break;
case 104: NMT->LEDdoubleFlash = -20; break;
default: break;
}
switch(++NMT->LEDtripleFlash){
case 4: NMT->LEDtripleFlash = -104; break;
case -100: NMT->LEDtripleFlash = 100; break;
case 104: NMT->LEDtripleFlash = -114; break;
case -110: NMT->LEDtripleFlash = 110; break;
case 114: NMT->LEDtripleFlash = -20; break;
default: break;
}
switch(++NMT->LEDquadrupleFlash){
case 4: NMT->LEDquadrupleFlash = -104; break;
case -100: NMT->LEDquadrupleFlash = 100; break;
case 104: NMT->LEDquadrupleFlash = -114; break;
case -110: NMT->LEDquadrupleFlash = 110; break;
case 114: NMT->LEDquadrupleFlash = -124; break;
case -120: NMT->LEDquadrupleFlash = 120; break;
case 124: NMT->LEDquadrupleFlash = -20; break;
default: break;
}
}
#endif /* CO_USE_LEDS */
/******************************************************************************/
CO_NMT_reset_cmd_t CO_NMT_process(
CO_NMT_t *NMT,
uint16_t timeDifference_ms,
uint16_t HBtime,
uint32_t NMTstartup,
uint8_t errorRegister,
const uint8_t errorBehavior[],
uint16_t *timerNext_ms)
{
uint8_t CANpassive;
uint8_t currentOperatingState = NMT->operatingState;
NMT->HBproducerTimer += timeDifference_ms;
/* Heartbeat producer message & Bootup message */
if((HBtime != 0 && NMT->HBproducerTimer >= HBtime) || NMT->operatingState == CO_NMT_INITIALIZING){
/* Start from the beginning. If OS is slow, time sliding may occur. However, heartbeat is
* not for synchronization, it is for health report. */
NMT->HBproducerTimer = 0;
NMT->HB_TXbuff->data[0] = NMT->operatingState;
CO_CANsend(NMT->HB_CANdev, NMT->HB_TXbuff);
if(NMT->operatingState == CO_NMT_INITIALIZING){
if(HBtime > NMT->firstHBTime) NMT->HBproducerTimer = HBtime - NMT->firstHBTime;
else NMT->HBproducerTimer = 0;
/* NMT slave self starting */
if (NMTstartup == 0x00000008U) NMT->operatingState = CO_NMT_OPERATIONAL;
else NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
}
}
/* Calculate, when next Heartbeat needs to be send and lower timerNext_ms if necessary. */
if(HBtime != 0 && timerNext_ms != NULL){
if(NMT->HBproducerTimer < HBtime){
uint16_t diff = HBtime - NMT->HBproducerTimer;
if(*timerNext_ms > diff){
*timerNext_ms = diff;
}
}else{
*timerNext_ms = 0;
}
}
/* CAN passive flag */
CANpassive = 0;
if(CO_isError(NMT->emPr->em, CO_EM_CAN_TX_BUS_PASSIVE) || CO_isError(NMT->emPr->em, CO_EM_CAN_RX_BUS_PASSIVE))
CANpassive = 1;
#ifdef CO_USE_LEDS
/* CANopen green RUN LED (DR 303-3) */
switch(NMT->operatingState){
case CO_NMT_STOPPED: NMT->LEDgreenRun = NMT->LEDsingleFlash; break;
case CO_NMT_PRE_OPERATIONAL: NMT->LEDgreenRun = NMT->LEDblinking; break;
case CO_NMT_OPERATIONAL: NMT->LEDgreenRun = 1; break;
default: break;
}
/* CANopen red ERROR LED (DR 303-3) */
if(CO_isError(NMT->emPr->em, CO_EM_CAN_TX_BUS_OFF))
NMT->LEDredError = 1;
else if(CO_isError(NMT->emPr->em, CO_EM_SYNC_TIME_OUT))
NMT->LEDredError = NMT->LEDtripleFlash;
else if(CO_isError(NMT->emPr->em, CO_EM_HEARTBEAT_CONSUMER) || CO_isError(NMT->emPr->em, CO_EM_HB_CONSUMER_REMOTE_RESET))
NMT->LEDredError = NMT->LEDdoubleFlash;
else if(CANpassive || CO_isError(NMT->emPr->em, CO_EM_CAN_BUS_WARNING))
NMT->LEDredError = NMT->LEDsingleFlash;
else if(errorRegister)
NMT->LEDredError = (NMT->LEDblinking>=0)?-1:1;
else
NMT->LEDredError = -1;
#endif /* CO_USE_LEDS */
/* in case of error enter pre-operational state */
if(errorBehavior && (NMT->operatingState == CO_NMT_OPERATIONAL)){
if(CANpassive && (errorBehavior[2] == 0 || errorBehavior[2] == 2)) errorRegister |= 0x10;
if(errorRegister){
/* Communication error */
if(errorRegister & CO_ERR_REG_COMM_ERR){
if(errorBehavior[1] == 0){
NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
}
else if(errorBehavior[1] == 2){
NMT->operatingState = CO_NMT_STOPPED;
}
else if(CO_isError(NMT->emPr->em, CO_EM_CAN_TX_BUS_OFF)
|| CO_isError(NMT->emPr->em, CO_EM_HEARTBEAT_CONSUMER)
|| CO_isError(NMT->emPr->em, CO_EM_HB_CONSUMER_REMOTE_RESET))
{
if(errorBehavior[0] == 0){
NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
}
else if(errorBehavior[0] == 2){
NMT->operatingState = CO_NMT_STOPPED;
}
}
}
/* Generic error */
if(errorRegister & CO_ERR_REG_GENERIC_ERR){
if (errorBehavior[3] == 0) NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
else if (errorBehavior[3] == 2) NMT->operatingState = CO_NMT_STOPPED;
}
/* Device profile error */
if(errorRegister & CO_ERR_REG_DEV_PROFILE){
if (errorBehavior[4] == 0) NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
else if (errorBehavior[4] == 2) NMT->operatingState = CO_NMT_STOPPED;
}
/* Manufacturer specific error */
if(errorRegister & CO_ERR_REG_MANUFACTURER){
if (errorBehavior[5] == 0) NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
else if (errorBehavior[5] == 2) NMT->operatingState = CO_NMT_STOPPED;
}
/* if operational state is lost, send HB immediately. */
if(NMT->operatingState != CO_NMT_OPERATIONAL)
NMT->HBproducerTimer = HBtime;
}
}
if(NMT->pFunctNMT!=NULL && currentOperatingState!=NMT->operatingState){
NMT->pFunctNMT(NMT->operatingState);
}
return NMT->resetCommand;
}
/******************************************************************************/
CO_NMT_internalState_t CO_NMT_getInternalState(
CO_NMT_t *NMT)
{
if(NMT != NULL){
return NMT->operatingState;
}
return CO_NMT_INITIALIZING;
}

View File

@ -0,0 +1,261 @@
/**
* CANopen Network management and Heartbeat producer protocol.
*
* @file CO_NMT_Heartbeat.h
* @ingroup CO_NMT_Heartbeat
* @author Janez Paternoster
* @copyright 2004 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CO_NMT_HEARTBEAT_H
#define CO_NMT_HEARTBEAT_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup CO_NMT_Heartbeat NMT and Heartbeat
* @ingroup CO_CANopen
* @{
*
* CANopen Network management and Heartbeat producer protocol.
*
* CANopen device can be in one of the #CO_NMT_internalState_t
* - Initializing. It is active before CANopen is initialized.
* - Pre-operational. All CANopen objects are active, except PDOs.
* - Operational. Process data objects (PDOs) are active too.
* - Stopped. Only Heartbeat producer and NMT consumer are active.
*
* NMT master can change the internal state of the devices by sending
* #CO_NMT_command_t.
*
* ###NMT message contents:
*
* Byte | Description
* -----|-----------------------------------------------------------
* 0 | #CO_NMT_command_t
* 1 | Node ID. If zero, command addresses all nodes.
*
* ###Heartbeat message contents:
*
* Byte | Description
* -----|-----------------------------------------------------------
* 0 | #CO_NMT_internalState_t
*
* @see #CO_Default_CAN_ID_t
*
* ###Status LED diodes
* Macros for @ref CO_NMT_statusLEDdiodes are also implemented in this object.
*/
/**
* @defgroup CO_NMT_statusLEDdiodes Status LED diodes
* @{
*
* Macros for status LED diodes.
*
* Helper macros for implementing status LED diodes are used by stack and can
* also be used by the application. If macro returns 1 LED should be ON,
* otherwise OFF. Function CO_NMT_blinkingProcess50ms() must be called cyclically
* to update the variables.
*/
#define LED_FLICKERING(NMT) (((NMT)->LEDflickering>=0) ? 1 : 0) /**< 10HZ (100MS INTERVAL) */
#define LED_BLINKING(NMT) (((NMT)->LEDblinking>=0) ? 1 : 0) /**< 2.5HZ (400MS INTERVAL) */
#define LED_SINGLE_FLASH(NMT) (((NMT)->LEDsingleFlash>=0) ? 1 : 0) /**< 200MS ON, 1000MS OFF */
#define LED_DOUBLE_FLASH(NMT) (((NMT)->LEDdoubleFlash>=0) ? 1 : 0) /**< 200MS ON, 200MS OFF, 200MS ON, 1000MS OFF */
#define LED_TRIPLE_FLASH(NMT) (((NMT)->LEDtripleFlash>=0) ? 1 : 0) /**< 200MS ON, 200MS OFF, 200MS ON, 200MS OFF, 200MS ON, 1000MS OFF */
#define LED_QUADRUPLE_FLASH(NMT)(((NMT)->LEDquadrupleFlash>=0) ? 1 : 0) /**< 200MS ON, 200MS OFF, 200MS ON, 200MS OFF, 200MS ON, 200MS OFF, 200MS ON, 1000MS OFF */
#define LED_GREEN_RUN(NMT) (((NMT)->LEDgreenRun>=0) ? 1 : 0) /**< CANOPEN RUN LED ACCORDING TO CIA DR 303-3 */
#define LED_RED_ERROR(NMT) (((NMT)->LEDredError>=0) ? 1 : 0) /**< CANopen error LED according to CiA DR 303-3 */
/** @} */
/**
* Internal network state of the CANopen node
*/
typedef enum{
CO_NMT_INITIALIZING = 0, /**< Device is initializing */
CO_NMT_PRE_OPERATIONAL = 127, /**< Device is in pre-operational state */
CO_NMT_OPERATIONAL = 5, /**< Device is in operational state */
CO_NMT_STOPPED = 4 /**< Device is stopped */
}CO_NMT_internalState_t;
/**
* Commands from NMT master.
*/
typedef enum{
CO_NMT_ENTER_OPERATIONAL = 1, /**< Start device */
CO_NMT_ENTER_STOPPED = 2, /**< Stop device */
CO_NMT_ENTER_PRE_OPERATIONAL = 128, /**< Put device into pre-operational */
CO_NMT_RESET_NODE = 129, /**< Reset device */
CO_NMT_RESET_COMMUNICATION = 130 /**< Reset CANopen communication on device */
}CO_NMT_command_t;
/**
* Return code for CO_NMT_process() that tells application code what to
* reset.
*/
typedef enum{
CO_RESET_NOT = 0,/**< Normal return, no action */
CO_RESET_COMM = 1,/**< Application must provide communication reset. */
CO_RESET_APP = 2,/**< Application must provide complete device reset */
CO_RESET_QUIT = 3 /**< Application must quit, no reset of microcontroller (command is not requested by the stack.) */
}CO_NMT_reset_cmd_t;
/**
* NMT consumer and Heartbeat producer object. It includes also variables for
* @ref CO_NMT_statusLEDdiodes. Object is initialized by CO_NMT_init().
*/
typedef struct{
uint8_t operatingState; /**< See @ref CO_NMT_internalState_t */
#ifdef CO_USE_LEDS
int8_t LEDflickering; /**< See @ref CO_NMT_statusLEDdiodes */
int8_t LEDblinking; /**< See @ref CO_NMT_statusLEDdiodes */
int8_t LEDsingleFlash; /**< See @ref CO_NMT_statusLEDdiodes */
int8_t LEDdoubleFlash; /**< See @ref CO_NMT_statusLEDdiodes */
int8_t LEDtripleFlash; /**< See @ref CO_NMT_statusLEDdiodes */
int8_t LEDquadrupleFlash; /**< See @ref CO_NMT_statusLEDdiodes */
int8_t LEDgreenRun; /**< See @ref CO_NMT_statusLEDdiodes */
int8_t LEDredError; /**< See @ref CO_NMT_statusLEDdiodes */
#endif /* CO_USE_LEDS */
uint8_t resetCommand; /**< If different than zero, device will reset */
uint8_t nodeId; /**< CANopen Node ID of this device */
uint16_t HBproducerTimer;/**< Internal timer for HB producer */
uint16_t firstHBTime; /**< From CO_NMT_init() */
CO_EMpr_t *emPr; /**< From CO_NMT_init() */
CO_CANmodule_t *HB_CANdev; /**< From CO_NMT_init() */
void (*pFunctNMT)(CO_NMT_internalState_t state); /**< From CO_NMT_initCallback() or NULL */
CO_CANtx_t *HB_TXbuff; /**< CAN transmit buffer */
}CO_NMT_t;
/**
* Initialize NMT and Heartbeat producer object.
*
* Function must be called in the communication reset section.
*
* @param NMT This object will be initialized.
* @param emPr Emergency main object.
* @param nodeId CANopen Node ID of this device.
* @param firstHBTime Time between bootup and first heartbeat message in milliseconds.
* If firstHBTime is greater than _Producer Heartbeat time_
* (object dictionary, index 0x1017), latter is used instead.
* @param NMT_CANdev CAN device for NMT reception.
* @param NMT_rxIdx Index of receive buffer in above CAN device.
* @param CANidRxNMT CAN identifier for NMT message.
* @param HB_CANdev CAN device for HB transmission.
* @param HB_txIdx Index of transmit buffer in the above CAN device.
* @param CANidTxHB CAN identifier for HB message.
*
* @return #CO_ReturnError_t CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
*/
CO_ReturnError_t CO_NMT_init(
CO_NMT_t *NMT,
CO_EMpr_t *emPr,
uint8_t nodeId,
uint16_t firstHBTime,
CO_CANmodule_t *NMT_CANdev,
uint16_t NMT_rxIdx,
uint16_t CANidRxNMT,
CO_CANmodule_t *HB_CANdev,
uint16_t HB_txIdx,
uint16_t CANidTxHB);
/**
* Initialize NMT callback function.
*
* Function initializes optional callback function, which is called after
* NMT State change has occured. Function may wake up external task which
* handles NMT events.
* The first call is made immediately to give the consumer the current NMT state.
*
* @remark Be aware that the callback function is run inside the CAN receive
* function context. Depending on the driver, this might be inside an interrupt!
*
* @param NMT This object.
* @param pFunctNMT Pointer to the callback function. Not called if NULL.
*/
void CO_NMT_initCallback(
CO_NMT_t *NMT,
void (*pFunctNMT)(CO_NMT_internalState_t state));
/**
* Calculate blinking bytes.
*
* Function must be called cyclically every 50 milliseconds. See @ref CO_NMT_statusLEDdiodes.
*
* @param NMT NMT object.
*/
#ifdef CO_USE_LEDS
void CO_NMT_blinkingProcess50ms(CO_NMT_t *NMT);
#endif /* CO_USE_LEDS */
/**
* Process received NMT and produce Heartbeat messages.
*
* Function must be called cyclically.
*
* @param NMT This object.
* @param timeDifference_ms Time difference from previous function call in [milliseconds].
* @param HBtime _Producer Heartbeat time_ (object dictionary, index 0x1017).
* @param NMTstartup _NMT startup behavior_ (object dictionary, index 0x1F80).
* @param errorRegister _Error register_ (object dictionary, index 0x1001).
* @param errorBehavior pointer to _Error behavior_ array (object dictionary, index 0x1029).
* Object controls, if device should leave NMT operational state.
* Length of array must be 6. If pointer is NULL, no calculation is made.
* @param timerNext_ms Return value - info to OS - see CO_process().
*
* @return #CO_NMT_reset_cmd_t
*/
CO_NMT_reset_cmd_t CO_NMT_process(
CO_NMT_t *NMT,
uint16_t timeDifference_ms,
uint16_t HBtime,
uint32_t NMTstartup,
uint8_t errorRegister,
const uint8_t errorBehavior[],
uint16_t *timerNext_ms);
/**
* Query current NMT state
*
* @param NMT This object.
*
* @return #CO_NMT_internalState_t
*/
CO_NMT_internalState_t CO_NMT_getInternalState(
CO_NMT_t *NMT);
#ifdef __cplusplus
}
#endif /*__cplusplus*/
/** @} */
#endif

1029
components/canopen/CO_PDO.c Normal file

File diff suppressed because it is too large Load Diff

387
components/canopen/CO_PDO.h Normal file
View File

@ -0,0 +1,387 @@
/**
* CANopen Process Data Object protocol.
*
* @file CO_PDO.h
* @ingroup CO_PDO
* @author Janez Paternoster
* @copyright 2004 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CO_PDO_H
#define CO_PDO_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup CO_PDO PDO
* @ingroup CO_CANopen
* @{
*
* CANopen Process Data Object protocol.
*
* Process data objects are used for real-time data transfer with no protocol
* overhead.
*
* TPDO with specific identifier is transmitted by one device and recieved by
* zero or more devices as RPDO. PDO communication parameters(COB-ID,
* transmission type, etc.) are in Object Dictionary at index 0x1400+ and
* 0x1800+. PDO mapping parameters (size and contents of the PDO) are in Object
* Dictionary at index 0x1600+ and 0x1A00+.
*
* Features of the PDO as implemented here, in CANopenNode:
* - Dynamic PDO mapping.
* - Map granularity of one byte.
* - After RPDO is received from CAN bus, its data are copied to buffer.
* Function CO_RPDO_process() (called by application) copies data to
* mapped objects in Object Dictionary. Synchronous RPDOs are processed AFTER
* reception of the next SYNC message.
* - Function CO_TPDO_process() (called by application) sends TPDO if
* necessary. There are possible different transmission types, including
* automatic detection of Change of State of specific variable.
*/
/**
* RPDO communication parameter. The same as record from Object dictionary (index 0x1400+).
*/
typedef struct{
uint8_t maxSubIndex; /**< Equal to 2 */
/** Communication object identifier for message received. Meaning of the specific bits:
- Bit 0-10: COB-ID for PDO, to change it bit 31 must be set.
- Bit 11-29: set to 0 for 11 bit COB-ID.
- Bit 30: If true, rtr are NOT allowed for PDO.
- Bit 31: If true, node does NOT use the PDO. */
uint32_t COB_IDUsedByRPDO;
/** Transmission type. Values:
- 0-240: Reciving is synchronous, process after next reception of the SYNC object.
- 241-253: Not used.
- 254: Manufacturer specific.
- 255: Asynchronous. */
uint8_t transmissionType;
}CO_RPDOCommPar_t;
/**
* RPDO mapping parameter. The same as record from Object dictionary (index 0x1600+).
*/
typedef struct{
/** Actual number of mapped objects from 0 to 8. To change mapped object,
this value must be 0. */
uint8_t numberOfMappedObjects;
/** Location and size of the mapped object. Bit meanings `0xIIIISSLL`:
- Bit 0-7: Data Length in bits.
- Bit 8-15: Subindex from object distionary.
- Bit 16-31: Index from object distionary. */
uint32_t mappedObject1;
uint32_t mappedObject2; /**< Same */
uint32_t mappedObject3; /**< Same */
uint32_t mappedObject4; /**< Same */
uint32_t mappedObject5; /**< Same */
uint32_t mappedObject6; /**< Same */
uint32_t mappedObject7; /**< Same */
uint32_t mappedObject8; /**< Same */
}CO_RPDOMapPar_t;
/**
* TPDO communication parameter. The same as record from Object dictionary (index 0x1800+).
*/
typedef struct{
uint8_t maxSubIndex; /**< Equal to 6 */
/** Communication object identifier for transmitting message. Meaning of the specific bits:
- Bit 0-10: COB-ID for PDO, to change it bit 31 must be set.
- Bit 11-29: set to 0 for 11 bit COB-ID.
- Bit 30: If true, rtr are NOT allowed for PDO.
- Bit 31: If true, node does NOT use the PDO. */
uint32_t COB_IDUsedByTPDO;
/** Transmission type. Values:
- 0: Transmiting is synchronous, specification in device profile.
- 1-240: Transmiting is synchronous after every N-th SYNC object.
- 241-251: Not used.
- 252-253: Transmited only on reception of Remote Transmission Request.
- 254: Manufacturer specific.
- 255: Asinchronous, specification in device profile. */
uint8_t transmissionType;
/** Minimum time between transmissions of the PDO in 100micro seconds.
Zero disables functionality. */
uint16_t inhibitTime;
/** Not used */
uint8_t compatibilityEntry;
/** Time between periodic transmissions of the PDO in milliseconds.
Zero disables functionality. */
uint16_t eventTimer;
/** Used with numbered SYNC messages. Values:
- 0: Counter of the SYNC message shall not be processed.
- 1-240: The SYNC message with the counter value equal to this value
shall be regarded as the first received SYNC message. */
uint8_t SYNCStartValue;
}CO_TPDOCommPar_t;
/**
* TPDO mapping parameter. The same as record from Object dictionary (index 0x1A00+).
*/
typedef struct{
/** Actual number of mapped objects from 0 to 8. To change mapped object,
this value must be 0. */
uint8_t numberOfMappedObjects;
/** Location and size of the mapped object. Bit meanings `0xIIIISSLL`:
- Bit 0-7: Data Length in bits.
- Bit 8-15: Subindex from object distionary.
- Bit 16-31: Index from object distionary. */
uint32_t mappedObject1;
uint32_t mappedObject2; /**< Same */
uint32_t mappedObject3; /**< Same */
uint32_t mappedObject4; /**< Same */
uint32_t mappedObject5; /**< Same */
uint32_t mappedObject6; /**< Same */
uint32_t mappedObject7; /**< Same */
uint32_t mappedObject8; /**< Same */
}CO_TPDOMapPar_t;
/**
* RPDO object.
*/
typedef struct{
CO_EM_t *em; /**< From CO_RPDO_init() */
CO_SDO_t *SDO; /**< From CO_RPDO_init() */
CO_SYNC_t *SYNC; /**< From CO_RPDO_init() */
const CO_RPDOCommPar_t *RPDOCommPar;/**< From CO_RPDO_init() */
const CO_RPDOMapPar_t *RPDOMapPar; /**< From CO_RPDO_init() */
uint8_t *operatingState; /**< From CO_RPDO_init() */
uint8_t nodeId; /**< From CO_RPDO_init() */
uint16_t defaultCOB_ID; /**< From CO_RPDO_init() */
uint8_t restrictionFlags;/**< From CO_RPDO_init() */
/** True, if PDO is enabled and valid */
bool_t valid;
/** True, if PDO synchronous (transmissionType <= 240) */
bool_t synchronous;
/** Data length of the received PDO message. Calculated from mapping */
uint8_t dataLength;
/** Pointers to 8 data objects, where PDO will be copied */
uint8_t *mapPointer[8];
/** Variable indicates, if new PDO message received from CAN bus. */
volatile void *CANrxNew[2];
/** 8 data bytes of the received message. */
uint8_t CANrxData[2][8];
CO_CANmodule_t *CANdevRx; /**< From CO_RPDO_init() */
uint16_t CANdevRxIdx; /**< From CO_RPDO_init() */
}CO_RPDO_t;
/**
* TPDO object.
*/
typedef struct{
CO_EM_t *em; /**< From CO_TPDO_init() */
CO_SDO_t *SDO; /**< From CO_TPDO_init() */
CO_SYNC_t *SYNC; /**< From CO_TPDO_init() */
const CO_TPDOCommPar_t *TPDOCommPar;/**< From CO_TPDO_init() */
const CO_TPDOMapPar_t *TPDOMapPar; /**< From CO_TPDO_init() */
uint8_t *operatingState; /**< From CO_TPDO_init() */
uint8_t nodeId; /**< From CO_TPDO_init() */
uint16_t defaultCOB_ID; /**< From CO_TPDO_init() */
uint8_t restrictionFlags;/**< From CO_TPDO_init() */
bool_t valid; /**< True, if PDO is enabled and valid */
/** Data length of the transmitting PDO message. Calculated from mapping */
uint8_t dataLength;
/** If application set this flag, PDO will be later sent by
function CO_TPDO_process(). Depends on transmission type. */
uint8_t sendRequest;
/** Pointers to 8 data objects, where PDO will be copied */
uint8_t *mapPointer[8];
/** Each flag bit is connected with one mapPointer. If flag bit
is true, CO_TPDO_process() functiuon will send PDO if
Change of State is detected on value pointed by that mapPointer */
uint8_t sendIfCOSFlags;
/** SYNC counter used for PDO sending */
uint8_t syncCounter;
/** Inhibit timer used for inhibit PDO sending translated to microseconds */
uint32_t inhibitTimer;
/** Event timer used for PDO sending translated to microseconds */
uint32_t eventTimer;
CO_CANmodule_t *CANdevTx; /**< From CO_TPDO_init() */
CO_CANtx_t *CANtxBuff; /**< CAN transmit buffer inside CANdev */
uint16_t CANdevTxIdx; /**< From CO_TPDO_init() */
}CO_TPDO_t;
/**
* Initialize RPDO object.
*
* Function must be called in the communication reset section.
*
* @param RPDO This object will be initialized.
* @param em Emergency object.
* @param SDO SDO server object.
* @param operatingState Pointer to variable indicating CANopen device NMT internal state.
* @param nodeId CANopen Node ID of this device. If default COB_ID is used, value will be added.
* @param defaultCOB_ID Default COB ID for this PDO (without NodeId).
* See #CO_Default_CAN_ID_t
* @param restrictionFlags Flag bits indicates, how PDO communication
* and mapping parameters are handled:
* - Bit1: If true, communication parameters are writeable only in pre-operational NMT state.
* - Bit2: If true, mapping parameters are writeable only in pre-operational NMT state.
* - Bit3: If true, communication parameters are read-only.
* - Bit4: If true, mapping parameters are read-only.
* @param RPDOCommPar Pointer to _RPDO communication parameter_ record from Object
* dictionary (index 0x1400+).
* @param RPDOMapPar Pointer to _RPDO mapping parameter_ record from Object
* dictionary (index 0x1600+).
* @param idx_RPDOCommPar Index in Object Dictionary.
* @param idx_RPDOMapPar Index in Object Dictionary.
* @param CANdevRx CAN device for PDO reception.
* @param CANdevRxIdx Index of receive buffer in the above CAN device.
*
* @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
*/
CO_ReturnError_t CO_RPDO_init(
CO_RPDO_t *RPDO,
CO_EM_t *em,
CO_SDO_t *SDO,
CO_SYNC_t *SYNC,
uint8_t *operatingState,
uint8_t nodeId,
uint16_t defaultCOB_ID,
uint8_t restrictionFlags,
const CO_RPDOCommPar_t *RPDOCommPar,
const CO_RPDOMapPar_t *RPDOMapPar,
uint16_t idx_RPDOCommPar,
uint16_t idx_RPDOMapPar,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdx);
/**
* Initialize TPDO object.
*
* Function must be called in the communication reset section.
*
* @param TPDO This object will be initialized.
* @param em Emergency object.
* @param SDO SDO object.
* @param operatingState Pointer to variable indicating CANopen device NMT internal state.
* @param nodeId CANopen Node ID of this device. If default COB_ID is used, value will be added.
* @param defaultCOB_ID Default COB ID for this PDO (without NodeId).
* See #CO_Default_CAN_ID_t
* @param restrictionFlags Flag bits indicates, how PDO communication
* and mapping parameters are handled:
* - Bit1: If true, communication parameters are writeable only in pre-operational NMT state.
* - Bit2: If true, mapping parameters are writeable only in pre-operational NMT state.
* - Bit3: If true, communication parameters are read-only.
* - Bit4: If true, mapping parameters are read-only.
* @param TPDOCommPar Pointer to _TPDO communication parameter_ record from Object
* dictionary (index 0x1400+).
* @param TPDOMapPar Pointer to _TPDO mapping parameter_ record from Object
* dictionary (index 0x1600+).
* @param idx_TPDOCommPar Index in Object Dictionary.
* @param idx_TPDOMapPar Index in Object Dictionary.
* @param CANdevTx CAN device used for PDO transmission.
* @param CANdevTxIdx Index of transmit buffer in the above CAN device.
*
* @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
*/
CO_ReturnError_t CO_TPDO_init(
CO_TPDO_t *TPDO,
CO_EM_t *em,
CO_SDO_t *SDO,
CO_SYNC_t *SYNC,
uint8_t *operatingState,
uint8_t nodeId,
uint16_t defaultCOB_ID,
uint8_t restrictionFlags,
const CO_TPDOCommPar_t *TPDOCommPar,
const CO_TPDOMapPar_t *TPDOMapPar,
uint16_t idx_TPDOCommPar,
uint16_t idx_TPDOMapPar,
CO_CANmodule_t *CANdevTx,
uint16_t CANdevTxIdx);
/**
* Verify Change of State of the PDO.
*
* Function verifies if variable mapped to TPDO has changed its value. Verified
* are only variables, which has set attribute _CO_ODA_TPDO_DETECT_COS_ in
* #CO_SDO_OD_attributes_t.
*
* Function may be called by application just before CO_TPDO_process() function,
* for example: `TPDOx->sendRequest = CO_TPDOisCOS(TPDOx); CO_TPDO_process(TPDOx, ....`
*
* @param TPDO TPDO object.
*
* @return True if COS was detected.
*/
uint8_t CO_TPDOisCOS(CO_TPDO_t *TPDO);
/**
* Send TPDO message.
*
* Function prepares TPDO data from Object Dictionary variables. It should not
* be called by application, it is called from CO_TPDO_process().
*
*
* @param TPDO TPDO object.
*
* @return Same as CO_CANsend().
*/
int16_t CO_TPDOsend(CO_TPDO_t *TPDO);
/**
* Process received PDO messages.
*
* Function must be called cyclically in any NMT state. It copies data from RPDO
* to Object Dictionary variables if: new PDO receives and PDO is valid and NMT
* operating state is operational. It does not verify _transmission type_.
*
* @param RPDO This object.
* @param syncWas True, if CANopen SYNC message was just received or transmitted.
*/
void CO_RPDO_process(CO_RPDO_t *RPDO, bool_t syncWas);
/**
* Process transmitting PDO messages.
*
* Function must be called cyclically in any NMT state. It prepares and sends
* TPDO if necessary. If Change of State needs to be detected, function
* CO_TPDOisCOS() must be called before.
*
* @param TPDO This object.
* @param SYNC SYNC object. Ignored if NULL.
* @param syncWas True, if CANopen SYNC message was just received or transmitted.
* @param timeDifference_us Time difference from previous function call in [microseconds].
*/
void CO_TPDO_process(
CO_TPDO_t *TPDO,
bool_t syncWas,
uint32_t timeDifference_us);
#ifdef __cplusplus
}
#endif /*__cplusplus*/
/** @} */
#endif

1488
components/canopen/CO_SDO.c Normal file

File diff suppressed because it is too large Load Diff

961
components/canopen/CO_SDO.h Normal file
View File

@ -0,0 +1,961 @@
/**
* CANopen Service Data Object - server protocol.
*
* @file CO_SDO.h
* @ingroup CO_SDO
* @author Janez Paternoster
* @copyright 2004 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CO_SDO_H
#define CO_SDO_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup CO_SDO SDO server
* @ingroup CO_CANopen
* @{
*
* CANopen Service Data Object - server protocol.
*
* Service data objects (SDOs) allow the access to any entry of the CANopen
* Object dictionary. An SDO establishes a peer-to-peer communication channel
* between two devices. In addition, the SDO protocol enables to transfer any
* amount of data in a segmented way. Therefore the SDO protocol is mainly used
* in order to communicate configuration data.
*
* All CANopen devices must have implemented SDO server and first SDO server
* channel. Servers serves data from Object dictionary. Object dictionary
* is a collection of variables, arrays or records (structures), which can be
* used by the stack or by the application. This file (CO_SDO.h) implements
* SDO server.
*
* SDO client can be (optionally) implemented on one (or multiple, if multiple
* SDO channels are used) device in CANopen network. Usually this is master
* device and provides also some kind of user interface, so configuration of
* the network is possible. Code for the SDO client is in file CO_SDOmaster.h.
*
* SDO communication cycle is initiated by the client. Client can upload (read) data
* from device or can download (write) data to device. If data are less or equal
* of 4 bytes long, communication is finished by one server response (expedited
* transfer). If data are longer, they are split into multiple segments of
* request/response pairs (normal or segmented transfer). For longer data there
* is also a block transfer protocol, which transfers larger block of data in
* secure way with little protocol overhead. If error occurs during SDO transfer
* #CO_SDO_abortCode_t is send by client or server and transfer is terminated.
*/
/**
* @defgroup CO_SDO_messageContents SDO message contents
*
* Excerpt from CiA DS301, V4.2.
*
* For CAN identifier see #CO_Default_CAN_ID_t
*
* Expedited transfer is used for transmission of up to 4 data bytes. It consists
* of one SDO request and one response. For longer variables is used segmented
* or block transfer.
*
* ####Initiate SDO download (client request)
* - byte 0: SDO command specifier. 8 bits: `0010nnes` (nn: if e=s=1,
* number of data bytes, that do *not* contain data; e=1 for
* expedited transfer; s=1 if data size is indicated).
* - byte 1..2: Object index.
* - byte 3: Object subIndex.
* - byte 4..7: Expedited data or data size if segmented transfer.
*
* ####Initiate SDO download (server response)
* - byte 0: SDO command specifier. 8 bits: `01100000`.
* - byte 1..2: Object index.
* - byte 3: Object subIndex.
* - byte 4..7: reserved.
*
* ####Download SDO segment (client request)
* - byte 0: SDO command specifier. 8 bits: `000tnnnc` (t: toggle bit set
* to 0 in first segment; nnn: number of data bytes, that do
* *not* contain data; c=1 if this is the last segment).
* - byte 1..7: Data segment.
*
* ####Download SDO segment (server response)
* - byte 0: SDO command specifier. 8 bits: `001t0000` (t: toggle bit set
* to 0 in first segment).
* - byte 1..7: Reserved.
*
* ####Initiate SDO upload (client request)
* - byte 0: SDO command specifier. 8 bits: `01000000`.
* - byte 1..2: Object index.
* - byte 3: Object subIndex.
* - byte 4..7: Reserved.
*
* ####Initiate SDO upload (server response)
* - byte 0: SDO command specifier. 8 bits: `0100nnes` (nn: if e=s=1,
* number of data bytes, that do *not* contain data; e=1 for
* expedited transfer; s=1 if data size is indicated).
* - byte 1..2: Object index.
* - byte 3: Object subIndex.
* - byte 4..7: reserved.
*
* ####Upload SDO segment (client request)
* - byte 0: SDO command specifier. 8 bits: `011t0000` (t: toggle bit set
* to 0 in first segment).
* - byte 1..7: Reserved.
*
* ####Upload SDO segment (server response)
* - byte 0: SDO command specifier. 8 bits: `000tnnnc` (t: toggle bit set
* to 0 in first segment; nnn: number of data bytes, that do
* *not* contain data; c=1 if this is the last segment).
* - byte 1..7: Data segment.
*
* ####Abort SDO transfer (client or server)
* - byte 0: SDO command specifier. 8 bits: `10000000`.
* - byte 1..2: Object index.
* - byte 3: Object subIndex.
* - byte 4..7: #CO_SDO_abortCode_t.
*
* ####Block transfer
* See DS301 V4.2.
*/
/**
* SDO abort codes.
*
* Send with Abort SDO transfer message.
*
* The abort codes not listed here are reserved.
*/
typedef enum{
CO_SDO_AB_NONE = 0x00000000UL, /**< 0x00000000, No abort */
CO_SDO_AB_TOGGLE_BIT = 0x05030000UL, /**< 0x05030000, Toggle bit not altered */
CO_SDO_AB_TIMEOUT = 0x05040000UL, /**< 0x05040000, SDO protocol timed out */
CO_SDO_AB_CMD = 0x05040001UL, /**< 0x05040001, Command specifier not valid or unknown */
CO_SDO_AB_BLOCK_SIZE = 0x05040002UL, /**< 0x05040002, Invalid block size in block mode */
CO_SDO_AB_SEQ_NUM = 0x05040003UL, /**< 0x05040003, Invalid sequence number in block mode */
CO_SDO_AB_CRC = 0x05040004UL, /**< 0x05040004, CRC error (block mode only) */
CO_SDO_AB_OUT_OF_MEM = 0x05040005UL, /**< 0x05040005, Out of memory */
CO_SDO_AB_UNSUPPORTED_ACCESS = 0x06010000UL, /**< 0x06010000, Unsupported access to an object */
CO_SDO_AB_WRITEONLY = 0x06010001UL, /**< 0x06010001, Attempt to read a write only object */
CO_SDO_AB_READONLY = 0x06010002UL, /**< 0x06010002, Attempt to write a read only object */
CO_SDO_AB_NOT_EXIST = 0x06020000UL, /**< 0x06020000, Object does not exist */
CO_SDO_AB_NO_MAP = 0x06040041UL, /**< 0x06040041, Object cannot be mapped to the PDO */
CO_SDO_AB_MAP_LEN = 0x06040042UL, /**< 0x06040042, Number and length of object to be mapped exceeds PDO length */
CO_SDO_AB_PRAM_INCOMPAT = 0x06040043UL, /**< 0x06040043, General parameter incompatibility reasons */
CO_SDO_AB_DEVICE_INCOMPAT = 0x06040047UL, /**< 0x06040047, General internal incompatibility in device */
CO_SDO_AB_HW = 0x06060000UL, /**< 0x06060000, Access failed due to hardware error */
CO_SDO_AB_TYPE_MISMATCH = 0x06070010UL, /**< 0x06070010, Data type does not match, length of service parameter does not match */
CO_SDO_AB_DATA_LONG = 0x06070012UL, /**< 0x06070012, Data type does not match, length of service parameter too high */
CO_SDO_AB_DATA_SHORT = 0x06070013UL, /**< 0x06070013, Data type does not match, length of service parameter too short */
CO_SDO_AB_SUB_UNKNOWN = 0x06090011UL, /**< 0x06090011, Sub index does not exist */
CO_SDO_AB_INVALID_VALUE = 0x06090030UL, /**< 0x06090030, Invalid value for parameter (download only). */
CO_SDO_AB_VALUE_HIGH = 0x06090031UL, /**< 0x06090031, Value range of parameter written too high */
CO_SDO_AB_VALUE_LOW = 0x06090032UL, /**< 0x06090032, Value range of parameter written too low */
CO_SDO_AB_MAX_LESS_MIN = 0x06090036UL, /**< 0x06090036, Maximum value is less than minimum value. */
CO_SDO_AB_NO_RESOURCE = 0x060A0023UL, /**< 0x060A0023, Resource not available: SDO connection */
CO_SDO_AB_GENERAL = 0x08000000UL, /**< 0x08000000, General error */
CO_SDO_AB_DATA_TRANSF = 0x08000020UL, /**< 0x08000020, Data cannot be transferred or stored to application */
CO_SDO_AB_DATA_LOC_CTRL = 0x08000021UL, /**< 0x08000021, Data cannot be transferred or stored to application because of local control */
CO_SDO_AB_DATA_DEV_STATE = 0x08000022UL, /**< 0x08000022, Data cannot be transferred or stored to application because of present device state */
CO_SDO_AB_DATA_OD = 0x08000023UL, /**< 0x08000023, Object dictionary not present or dynamic generation fails */
CO_SDO_AB_NO_DATA = 0x08000024UL /**< 0x08000024, No data available */
}CO_SDO_abortCode_t;
/**
* @defgroup CO_SDO_objectDictionary Object dictionary
*
* CANopen Object dictionary implementation in CANopenNode.
*
* CANopen Object dictionary is a collection of different data items, which can
* be used by the stack or by the application.
*
* Each Object dictionary entry is located under 16-bit index, as specified
* by the CANopen:
* - 0x0001..0x025F: Data type definitions.
* - 0x1000..0x1FFF: Communication profile area.
* - 0x2000..0x5FFF: Manufacturer-specific profile area.
* - 0x6000..0x9FFF: Standardized device profile area for eight logical devices.
* - 0xA000..0xAFFF: Standardized network variable area.
* - 0xB000..0xBFFF: Standardized system variable area.
* - Other: Reserved.
*
* If Object dictionary entry has complex data type (array or structure),
* then 8-bit subIndex specifies the sub-member of the entry. In that case
* subIndex 0x00 is encoded as uint8_t and specifies the highest available
* subIndex with that entry. Subindex 0xFF has special meaning in the standard
* and is not supported by CANopenNode.
*
* ####Object type of one Object dictionary entry
* - NULL: Not used by CANopenNode.
* - DOMAIN: Block of data of variable length. Data and length are
* under control of the application.
* - DEFTYPE: Definition of CANopen basic data type, for example
* INTEGER16.
* - DEFSTRUCT: Definition of complex data type - structure, which is
* used with RECORD.
* - VAR: Variable of CANopen basic data type. Subindex is 0.
* - ARRAY: Array of multiple variables of the same CANopen basic
* data type. Subindex 1..arrayLength specifies sub-member.
* - RECORD: Record or structure of multiple variables of different
* CANopen basic data type. Subindex specifies sub-member.
*
*
* ####Implementation in CANopenNode
* Object dictionary in CANopenNode is implemented in CO_OD.h and CO_OD.c files.
* These files are application specific and must be generated by Object
* dictionary editor (application is included by the stack).
*
* CO_OD.h and CO_OD.c files include:
* - Structure definitions for records.
* - Global declaration and initialization of all variables, arrays and records
* mapped to Object dictionary. Variables are distributed in multiple objects,
* depending on memory location. This eases storage to different memories in
* microcontroller, like eeprom or flash.
* - Constant array of multiple Object dictionary entries of type
* CO_OD_entry_t. If object type is record, then entry includes additional
* constant array with members of type CO_OD_entryRecord_t. Each OD entry
* includes information: index, maxSubIndex, #CO_SDO_OD_attributes_t, data size and
* pointer to variable.
*
*
* Function CO_SDO_init() initializes object CO_SDO_t, which includes SDO
* server and Object dictionary.
*
* Application doesn't need to know anything about the Object dictionary. It can
* use variables specified in CO_OD.h file directly. If it needs more control
* over the CANopen communication with the variables, it can configure additional
* functionality with function CO_OD_configure(). Additional functionality
* include: @ref CO_SDO_OD_function and #CO_SDO_OD_flags_t.
*
* Interface to Object dictionary is provided by following functions: CO_OD_find()
* finds OD entry by index, CO_OD_getLength() returns length of variable,
* CO_OD_getAttribute returns attribute and CO_OD_getDataPointer() returns pointer
* to data. These functions are used by SDO server and by PDO configuration. They
* can also be used to access the OD by index like this.
*
* \code{.c}
* index = CO_OD_find(CO->SDO[0], OD_H1001_ERR_REG);
* if (index == 0xffff) {
* return;
* }
* length = CO_OD_getLength(CO->SDO[0], index, 1);
* if (length != sizeof(new_data)) {
* return;
* }
*
* p = CO_OD_getDataPointer(CO->SDO[0], index, 1);
* if (p == NULL) {
* return;
* }
* CO_LOCK_OD();
* *p = new_data;
* CO_UNLOCK_OD();
* \endcode
*
* Be aware that accessing the OD directly using CO_OD.h files is more CPU
* efficient as CO_OD_find() has to do a search everytime it is called.
*
*/
/**
* @defgroup CO_SDO_OD_function Object Dictionary function
*
* Optional application specific function, which may manipulate data downloaded
* or uploaded via SDO.
*
* Object dictionary function is external function defined by application or
* by other stack files. It may be registered for specific Object dictionary
* entry (with specific index). If it is registered, it is called (through
* function pointer) from SDO server. It may verify and manipulate data during
* SDO transfer. Object dictionary function can be registered by function
* CO_OD_configure().
*
* ####SDO download (writing to Object dictionary)
* After SDO client transfers data to the server, data are stored in internal
* buffer. If data contains multibyte variable and processor is big endian,
* then data bytes are swapped. Object dictionary function is called if
* registered. Data may be verified and manipulated inside that function. After
* function exits, data are copied to location as specified in CO_OD_entry_t.
*
* ####SDO upload (reading from Object dictionary)
* Before start of SDO upload, data are read from Object dictionary into
* internal buffer. If necessary, bytes are swapped.
* Object dictionary function is called if registered. Data may be
* manipulated inside that function. After function exits, data are
* transferred via SDO server.
*
* ####Domain data type
* If data type is domain, then length is not specified by Object dictionary.
* In that case Object dictionary function must be used. In case of
* download it must store the data in own location. In case of upload it must
* write the data (maximum size is specified by length) into data buffer and
* specify actual length. With domain data type it is possible to transfer
* data, which are longer than #CO_SDO_BUFFER_SIZE. In that case
* Object dictionary function is called multiple times between SDO transfer.
*
* ####Parameter to function:
* ODF_arg - Pointer to CO_ODF_arg_t object filled before function call.
*
* ####Return from function:
* - 0: Data transfer is successful
* - Different than 0: Failure. See #CO_SDO_abortCode_t.
*/
/**
* SDO buffer size.
*
* Size of the internal SDO buffer.
*
* Size must be at least equal to size of largest variable in @ref CO_SDO_objectDictionary.
* If data type is domain, data length is not limited to SDO buffer size. If
* block transfer is implemented, value should be set to 889.
*
* Value can be in range from 7 to 889 bytes.
*/
#ifndef CO_SDO_BUFFER_SIZE
#define CO_SDO_BUFFER_SIZE 32
#endif
/**
* Object Dictionary attributes. Bit masks for attribute in CO_OD_entry_t.
*/
typedef enum{
CO_ODA_MEM_ROM = 0x0001U, /**< Variable is located in ROM memory */
CO_ODA_MEM_RAM = 0x0002U, /**< Variable is located in RAM memory */
CO_ODA_MEM_EEPROM = 0x0003U, /**< Variable is located in EEPROM memory */
CO_ODA_READABLE = 0x0004U, /**< SDO server may read from the variable */
CO_ODA_WRITEABLE = 0x0008U, /**< SDO server may write to the variable */
CO_ODA_RPDO_MAPABLE = 0x0010U, /**< Variable is mappable for RPDO */
CO_ODA_TPDO_MAPABLE = 0x0020U, /**< Variable is mappable for TPDO */
CO_ODA_TPDO_DETECT_COS = 0x0040U, /**< If variable is mapped to any PDO, then
PDO is automatically send, if variable
changes its value */
CO_ODA_MB_VALUE = 0x0080U /**< True when variable is a multibyte value */
}CO_SDO_OD_attributes_t;
/**
* Common DS301 object dictionary entries.
*/
typedef enum{
OD_H1000_DEV_TYPE = 0x1000U,/**< Device type */
OD_H1001_ERR_REG = 0x1001U,/**< Error register */
OD_H1002_MANUF_STATUS_REG = 0x1002U,/**< Manufacturer status register */
OD_H1003_PREDEF_ERR_FIELD = 0x1003U,/**< Predefined error field */
OD_H1004_RSV = 0x1004U,/**< Reserved */
OD_H1005_COBID_SYNC = 0x1005U,/**< Sync message cob-id */
OD_H1006_COMM_CYCL_PERIOD = 0x1006U,/**< Communication cycle period */
OD_H1007_SYNC_WINDOW_LEN = 0x1007U,/**< Sync windows length */
OD_H1008_MANUF_DEV_NAME = 0x1008U,/**< Manufacturer device name */
OD_H1009_MANUF_HW_VERSION = 0x1009U,/**< Manufacturer hardware version */
OD_H100A_MANUF_SW_VERSION = 0x100AU,/**< Manufacturer software version */
OD_H100B_RSV = 0x100BU,/**< Reserved */
OD_H100C_GUARD_TIME = 0x100CU,/**< Guard time */
OD_H100D_LIFETIME_FACTOR = 0x100DU,/**< Life time factor */
OD_H100E_RSV = 0x100EU,/**< Reserved */
OD_H100F_RSV = 0x100FU,/**< Reserved */
OD_H1010_STORE_PARAM_FUNC = 0x1010U,/**< Store parameter in persistent memory function */
OD_H1011_REST_PARAM_FUNC = 0x1011U,/**< Restore default parameter function */
OD_H1012_COBID_TIME = 0x1012U,/**< Timestamp message cob-id */
OD_H1013_HIGH_RES_TIMESTAMP = 0x1013U,/**< High resolution timestamp */
OD_H1014_COBID_EMERGENCY = 0x1014U,/**< Emergency message cob-id */
OD_H1015_INHIBIT_TIME_MSG = 0x1015U,/**< Inhibit time message */
OD_H1016_CONSUMER_HB_TIME = 0x1016U,/**< Consumer heartbeat time */
OD_H1017_PRODUCER_HB_TIME = 0x1017U,/**< Producer heartbeat time */
OD_H1018_IDENTITY_OBJECT = 0x1018U,/**< Identity object */
OD_H1019_SYNC_CNT_OVERFLOW = 0x1019U,/**< Sync counter overflow value */
OD_H1020_VERIFY_CONFIG = 0x1020U,/**< Verify configuration */
OD_H1021_STORE_EDS = 0x1021U,/**< Store EDS */
OD_H1022_STORE_FORMAT = 0x1022U,/**< Store format */
OD_H1023_OS_CMD = 0x1023U,/**< OS command */
OD_H1024_OS_CMD_MODE = 0x1024U,/**< OS command mode */
OD_H1025_OS_DBG_INTERFACE = 0x1025U,/**< OS debug interface */
OD_H1026_OS_PROMPT = 0x1026U,/**< OS prompt */
OD_H1027_MODULE_LIST = 0x1027U,/**< Module list */
OD_H1028_EMCY_CONSUMER = 0x1028U,/**< Emergency consumer object */
OD_H1029_ERR_BEHAVIOR = 0x1029U,/**< Error behaviour */
OD_H1200_SDO_SERVER_PARAM = 0x1200U,/**< SDO server parameters */
OD_H1280_SDO_CLIENT_PARAM = 0x1280U,/**< SDO client parameters */
OD_H1400_RXPDO_1_PARAM = 0x1400U,/**< RXPDO communication parameter */
OD_H1401_RXPDO_2_PARAM = 0x1401U,/**< RXPDO communication parameter */
OD_H1402_RXPDO_3_PARAM = 0x1402U,/**< RXPDO communication parameter */
OD_H1403_RXPDO_4_PARAM = 0x1403U,/**< RXPDO communication parameter */
OD_H1600_RXPDO_1_MAPPING = 0x1600U,/**< RXPDO mapping parameters */
OD_H1601_RXPDO_2_MAPPING = 0x1601U,/**< RXPDO mapping parameters */
OD_H1602_RXPDO_3_MAPPING = 0x1602U,/**< RXPDO mapping parameters */
OD_H1603_RXPDO_4_MAPPING = 0x1603U,/**< RXPDO mapping parameters */
OD_H1800_TXPDO_1_PARAM = 0x1800U,/**< TXPDO communication parameter */
OD_H1801_TXPDO_2_PARAM = 0x1801U,/**< TXPDO communication parameter */
OD_H1802_TXPDO_3_PARAM = 0x1802U,/**< TXPDO communication parameter */
OD_H1803_TXPDO_4_PARAM = 0x1803U,/**< TXPDO communication parameter */
OD_H1A00_TXPDO_1_MAPPING = 0x1A00U,/**< TXPDO mapping parameters */
OD_H1A01_TXPDO_2_MAPPING = 0x1A01U,/**< TXPDO mapping parameters */
OD_H1A02_TXPDO_3_MAPPING = 0x1A02U,/**< TXPDO mapping parameters */
OD_H1A03_TXPDO_4_MAPPING = 0x1A03U /**< TXPDO mapping parameters */
}CO_ObjDicId_t;
/**
* Bit masks for flags associated with variable from @ref CO_SDO_objectDictionary.
*
* This additional functionality of any variable in @ref CO_SDO_objectDictionary can be
* enabled by function CO_OD_configure(). Location of the flag byte can be
* get from function CO_OD_getFlagsPointer().
*/
typedef enum{
/** Variable was written by RPDO. Flag can be cleared by application */
CO_ODFL_RPDO_WRITTEN = 0x01U,
/** Variable is mapped to TPDO */
CO_ODFL_TPDO_MAPPED = 0x02U,
/** Change of state bit, initially copy of attribute from CO_OD_entry_t.
If set and variable is mapped to TPDO, TPDO will be automatically send,
if variable changed */
CO_ODFL_TPDO_COS_ENABLE = 0x04U,
/** PDO send bit, can be set by application. If variable is mapped into
TPDO, TPDO will be send and bit will be cleared. */
CO_ODFL_TPDO_SEND = 0x08U,
/** Variable was accessed by SDO download */
CO_ODFL_SDO_DOWNLOADED = 0x10U,
/** Variable was accessed by SDO upload */
CO_ODFL_SDO_UPLOADED = 0x20U,
/** Reserved */
CO_ODFL_BIT_6 = 0x40U,
/** Reserved */
CO_ODFL_BIT_7 = 0x80U
}CO_SDO_OD_flags_t;
/**
* Internal states of the SDO server state machine
*/
typedef enum {
CO_SDO_ST_IDLE = 0x00U,
CO_SDO_ST_DOWNLOAD_INITIATE = 0x11U,
CO_SDO_ST_DOWNLOAD_SEGMENTED = 0x12U,
CO_SDO_ST_DOWNLOAD_BL_INITIATE = 0x14U,
CO_SDO_ST_DOWNLOAD_BL_SUBBLOCK = 0x15U,
CO_SDO_ST_DOWNLOAD_BL_SUB_RESP = 0x16U,
CO_SDO_ST_DOWNLOAD_BL_SUB_RESP_2 = 0x17U,
CO_SDO_ST_DOWNLOAD_BL_END = 0x18U,
CO_SDO_ST_UPLOAD_INITIATE = 0x21U,
CO_SDO_ST_UPLOAD_SEGMENTED = 0x22U,
CO_SDO_ST_UPLOAD_BL_INITIATE = 0x24U,
CO_SDO_ST_UPLOAD_BL_INITIATE_2 = 0x25U,
CO_SDO_ST_UPLOAD_BL_SUBBLOCK = 0x26U,
CO_SDO_ST_UPLOAD_BL_END = 0x27U
} CO_SDO_state_t;
/**
* Object for one entry with specific index in @ref CO_SDO_objectDictionary.
*/
typedef struct {
/** The index of Object from 0x1000 to 0xFFFF */
uint16_t index;
/** Number of (sub-objects - 1). If Object Type is variable, then
maxSubIndex is 0, otherwise maxSubIndex is equal or greater than 1. */
uint8_t maxSubIndex;
/** If Object Type is record, attribute is set to zero. Attribute for
each member is then set in special array with members of type
CO_OD_entryRecord_t. If Object Type is Array, attribute is common for
all array members. See #CO_SDO_OD_attributes_t. */
uint16_t attribute;
/** If Object Type is Variable, length is the length of variable in bytes.
If Object Type is Array, length is the length of one array member.
If Object Type is Record, length is zero. Length for each member is
set in special array with members of type CO_OD_entryRecord_t.
If Object Type is Domain, length is zero. Length is specified
by application in @ref CO_SDO_OD_function. */
uint16_t length;
/** If Object Type is Variable, pData is pointer to data.
If Object Type is Array, pData is pointer to data. Data doesn't
include Sub-Object 0.
If object type is Record, pData is pointer to special array
with members of type CO_OD_entryRecord_t.
If object type is Domain, pData is null. */
void *pData;
}CO_OD_entry_t;
/**
* Object for record type entry in @ref CO_SDO_objectDictionary.
*
* See CO_OD_entry_t.
*/
typedef struct{
/** See #CO_SDO_OD_attributes_t */
void *pData;
/** Length of variable in bytes. If object type is Domain, length is zero */
uint16_t attribute;
/** Pointer to data. If object type is Domain, pData is null */
uint16_t length;
}CO_OD_entryRecord_t;
/**
* Object contains all information about the object being transferred by SDO server.
*
* Object is used as an argument to @ref CO_SDO_OD_function. It is also
* part of the CO_SDO_t object.
*/
typedef struct{
/** Informative parameter. It may point to object, which is connected
with this OD entry. It can be used inside @ref CO_SDO_OD_function, ONLY
if it was registered by CO_OD_configure() function before. */
void *object;
/** SDO data buffer contains data, which are exchanged in SDO transfer.
@ref CO_SDO_OD_function may verify or manipulate that data before (after)
they are written to (read from) Object dictionary. Data have the same
endianes as processor. Pointer must NOT be changed. (Data up to length
can be changed.) */
uint8_t *data;
/** Pointer to location in object dictionary, where data are stored.
(informative reference to old data, read only). Data have the same
endianes as processor. If data type is Domain, this variable is null. */
const void *ODdataStorage;
/** Length of data in the above buffer. Read only, except for domain. If
data type is domain see @ref CO_SDO_OD_function for special rules by upload. */
uint16_t dataLength;
/** Attribute of object in Object dictionary (informative, must NOT be changed). */
uint16_t attribute;
/** Pointer to the #CO_SDO_OD_flags_t byte. */
uint8_t *pFlags;
/** Index of object in Object dictionary (informative, must NOT be changed). */
uint16_t index;
/** Subindex of object in Object dictionary (informative, must NOT be changed). */
uint8_t subIndex;
/** True, if SDO upload is in progress, false if SDO download is in progress. */
bool_t reading;
/** Used by domain data type. Indicates the first segment. Variable is informative. */
bool_t firstSegment;
/** Used by domain data type. If false by download, then application will
receive more segments during SDO communication cycle. If uploading,
application may set variable to false, so SDO server will call
@ref CO_SDO_OD_function again for filling the next data. */
bool_t lastSegment;
/** Used by domain data type. By upload @ref CO_SDO_OD_function may write total
data length, so this information will be send in SDO upload initiate phase. It
is not necessary to specify this variable. By download this variable contains
total data size, if size is indicated in SDO download initiate phase */
uint32_t dataLengthTotal;
/** Used by domain data type. In case of multiple segments, this indicates the offset
into the buffer this segment starts at. */
uint32_t offset;
}CO_ODF_arg_t;
/**
* Object is used as array inside CO_SDO_t, parallel to @ref CO_SDO_objectDictionary.
*
* Object is generated by function CO_OD_configure(). It is then used as
* extension to Object dictionary entry at specific index.
*/
typedef struct{
/** Pointer to @ref CO_SDO_OD_function */
CO_SDO_abortCode_t (*pODFunc)(CO_ODF_arg_t *ODF_arg);
/** Pointer to object, which will be passed to @ref CO_SDO_OD_function */
void *object;
/** Pointer to #CO_SDO_OD_flags_t. If object type is array or record, this
variable points to array with length equal to number of subindexes. */
uint8_t *flags;
}CO_OD_extension_t;
/**
* SDO server object.
*/
typedef struct{
/** 8 data bytes of the received message. */
uint8_t CANrxData[8];
/** SDO data buffer of size #CO_SDO_BUFFER_SIZE. */
uint8_t databuffer[CO_SDO_BUFFER_SIZE];
/** Internal flag indicates, that this object has own OD */
bool_t ownOD;
/** Pointer to the @ref CO_SDO_objectDictionary (array) */
const CO_OD_entry_t *OD;
/** Size of the @ref CO_SDO_objectDictionary */
uint16_t ODSize;
/** Pointer to array of CO_OD_extension_t objects. Size of the array is
equal to ODSize. */
CO_OD_extension_t *ODExtensions;
/** Offset in buffer of next data segment being read/written */
uint16_t bufferOffset;
/** Sequence number of OD entry as returned from CO_OD_find() */
uint16_t entryNo;
/** CO_ODF_arg_t object with additional variables. Reference to this object
is passed to @ref CO_SDO_OD_function */
CO_ODF_arg_t ODF_arg;
/** From CO_SDO_init() */
uint8_t nodeId;
/** Current internal state of the SDO server state machine #CO_SDO_state_t */
CO_SDO_state_t state;
/** Toggle bit in segmented transfer or block sequence in block transfer */
uint8_t sequence;
/** Timeout timer for SDO communication */
uint16_t timeoutTimer;
/** Number of segments per block with 1 <= blksize <= 127 */
uint8_t blksize;
/** True, if CRC calculation by block transfer is enabled */
bool_t crcEnabled;
/** Calculated CRC code */
uint16_t crc;
/** Length of data in the last segment in block upload */
uint8_t lastLen;
/** Indication timeout in sub-block transfer */
bool_t timeoutSubblockDownolad;
/** Indication end of block transfer */
bool_t endOfTransfer;
/** Variable indicates, if new SDO message received from CAN bus */
volatile void *CANrxNew;
/** From CO_SDO_initCallback() or NULL */
void (*pFunctSignal)(void);
/** From CO_SDO_init() */
CO_CANmodule_t *CANdevTx;
/** CAN transmit buffer inside CANdev for CAN tx message */
CO_CANtx_t *CANtxBuff;
}CO_SDO_t;
/**
* Helper union for manipulating data bytes.
*/
typedef union{
uint8_t u8[8]; /**< 8 bytes */
uint16_t u16[4]; /**< 4 words */
uint32_t u32[2]; /**< 2 double words */
}CO_bytes_t;
/**
* Helper function like memcpy.
*
* Function copies n data bytes from source to destination.
*
* @param dest Destination location.
* @param src Source location.
* @param size Number of data bytes to be copied (max 0xFFFF).
*/
void CO_memcpy(uint8_t dest[], const uint8_t src[], const uint16_t size);
/**
* Helper function like memset.
*
* Function fills destination with char "c".
*
* @param dest Destination location.
* @param c set value.
* @param size Number of data bytes to be copied (max 0xFFFF).
*/
void CO_memset(uint8_t dest[], uint8_t c, const uint16_t size);
/**
* Helper function returns uint16 from byte array.
*
* @param data Location of source data.
* @return Variable of type uint16_t.
*/
uint16_t CO_getUint16(const uint8_t data[]);
/**
* Helper function returns uint32 from byte array.
*
* @param data Location of source data.
* @return Variable of type uint32_t.
*/
uint32_t CO_getUint32(const uint8_t data[]);
/**
* Helper function writes uint16 to byte array.
*
* @param data Location of destination data.
* @param value Variable of type uint16_t to be written into data.
*/
void CO_setUint16(uint8_t data[], const uint16_t value);
/**
* Helper function writes uint32 to byte array.
*
* @param data Location of destination data.
* @param value Variable of type uint32_t to be written into data.
*/
void CO_setUint32(uint8_t data[], const uint32_t value);
/**
* Copy 2 data bytes from source to destination. Swap bytes if
* microcontroller is big-endian.
*
* @param dest Destination location.
* @param src Source location.
*/
void CO_memcpySwap2(void* dest, const void* src);
/**
* Copy 4 data bytes from source to destination. Swap bytes if
* microcontroller is big-endian.
*
* @param dest Destination location.
* @param src Source location.
*/
void CO_memcpySwap4(void* dest, const void* src);
/**
* Copy 8 data bytes from source to destination. Swap bytes if
* microcontroller is big-endian.
*
* @param dest Destination location.
* @param src Source location.
*/
void CO_memcpySwap8(void* dest, const void* src);
/**
* Initialize SDO object.
*
* Function must be called in the communication reset section.
*
* @param SDO This object will be initialized.
* @param COB_IDClientToServer COB ID for client to server for this SDO object.
* @param COB_IDServerToClient COB ID for server to client for this SDO object.
* @param ObjDictIndex_SDOServerParameter Index in Object dictionary.
* @param parentSDO Pointer to SDO object, which contains object dictionary and
* its extension. For first (default) SDO object this argument must be NULL.
* If this argument is specified, then OD, ODSize and ODExtensions arguments
* are ignored.
* @param OD Pointer to @ref CO_SDO_objectDictionary array defined externally.
* @param ODSize Size of the above array.
* @param ODExtensions Pointer to the externally defined array of the same size
* as ODSize.
* @param nodeId CANopen Node ID of this device.
* @param CANdevRx CAN device for SDO server reception.
* @param CANdevRxIdx Index of receive buffer in the above CAN device.
* @param CANdevTx CAN device for SDO server transmission.
* @param CANdevTxIdx Index of transmit buffer in the above CAN device.
*
* @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
*/
CO_ReturnError_t CO_SDO_init(
CO_SDO_t *SDO,
uint32_t COB_IDClientToServer,
uint32_t COB_IDServerToClient,
uint16_t ObjDictIndex_SDOServerParameter,
CO_SDO_t *parentSDO,
const CO_OD_entry_t OD[],
uint16_t ODSize,
CO_OD_extension_t ODExtensions[],
uint8_t nodeId,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdx,
CO_CANmodule_t *CANdevTx,
uint16_t CANdevTxIdx);
/**
* Initialize SDOrx callback function.
*
* Function initializes optional callback function, which is called after new
* message is received from the CAN bus. Function may wake up external task,
* which processes mainline CANopen functions.
*
* @param SDO This object.
* @param pFunctSignal Pointer to the callback function. Not called if NULL.
*/
void CO_SDO_initCallback(
CO_SDO_t *SDO,
void (*pFunctSignal)(void));
/**
* Process SDO communication.
*
* Function must be called cyclically.
*
* @param SDO This object.
* @param NMTisPreOrOperational Different than zero, if #CO_NMT_internalState_t is
* NMT_PRE_OPERATIONAL or NMT_OPERATIONAL.
* @param timeDifference_ms Time difference from previous function call in [milliseconds].
* @param SDOtimeoutTime Timeout time for SDO communication in milliseconds.
* @param timerNext_ms Return value - info to OS - see CO_process().
*
* @return 0: SDO server is idle.
* @return 1: SDO server is in transfer state.
* @return -1: SDO abort just occurred.
*/
int8_t CO_SDO_process(
CO_SDO_t *SDO,
bool_t NMTisPreOrOperational,
uint16_t timeDifference_ms,
uint16_t SDOtimeoutTime,
uint16_t *timerNext_ms);
/**
* Configure additional functionality to one @ref CO_SDO_objectDictionary entry.
*
* Additional functionality include: @ref CO_SDO_OD_function and
* #CO_SDO_OD_flags_t. It is optional feature and can be used on any object in
* Object dictionary. If OD entry does not exist, function returns silently.
*
* @param SDO This object.
* @param index Index of object in the Object dictionary.
* @param pODFunc Pointer to @ref CO_SDO_OD_function, specified by application.
* If NULL, @ref CO_SDO_OD_function will not be used on this object.
* @param object Pointer to object, which will be passed to @ref CO_SDO_OD_function.
* @param flags Pointer to array of #CO_SDO_OD_flags_t defined externally. If
* zero, #CO_SDO_OD_flags_t will not be used on this OD entry.
* @param flagsSize Size of the above array. It must be equal to number
* of sub-objects in object dictionary entry. Otherwise #CO_SDO_OD_flags_t will
* not be used on this OD entry.
*/
void CO_OD_configure(
CO_SDO_t *SDO,
uint16_t index,
CO_SDO_abortCode_t (*pODFunc)(CO_ODF_arg_t *ODF_arg),
void *object,
uint8_t *flags,
uint8_t flagsSize);
/**
* Find object with specific index in Object dictionary.
*
* @param SDO This object.
* @param index Index of the object in Object dictionary.
*
* @return Sequence number of the @ref CO_SDO_objectDictionary entry, 0xFFFF if not found.
*/
uint16_t CO_OD_find(CO_SDO_t *SDO, uint16_t index);
/**
* Get length of the given object with specific subIndex.
*
* @param SDO This object.
* @param entryNo Sequence number of OD entry as returned from CO_OD_find().
* @param subIndex Sub-index of the object in Object dictionary.
*
* @return Data length of the variable.
*/
uint16_t CO_OD_getLength(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex);
/**
* Get attribute of the given object with specific subIndex. See #CO_SDO_OD_attributes_t.
*
* If Object Type is array and subIndex is zero, function always returns
* 'read-only' attribute. An exception to this rule is ID1003 (Error field).
* However, this is supposed to be only written by network.
*
* @param SDO This object.
* @param entryNo Sequence number of OD entry as returned from CO_OD_find().
* @param subIndex Sub-index of the object in Object dictionary.
*
* @return Attribute of the variable.
*/
uint16_t CO_OD_getAttribute(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex);
/**
* Get pointer to data of the given object with specific subIndex.
*
* If Object Type is array and subIndex is zero, function returns pointer to
* object->maxSubIndex variable.
*
* @param SDO This object.
* @param entryNo Sequence number of OD entry as returned from CO_OD_find().
* @param subIndex Sub-index of the object in Object dictionary.
*
* @return Pointer to the variable in @ref CO_SDO_objectDictionary.
*/
void* CO_OD_getDataPointer(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex);
/**
* Get pointer to the #CO_SDO_OD_flags_t byte of the given object with
* specific subIndex.
*
* @param SDO This object.
* @param entryNo Sequence number of OD entry as returned from CO_OD_find().
* @param subIndex Sub-index of the object in Object dictionary.
*
* @return Pointer to the #CO_SDO_OD_flags_t of the variable.
*/
uint8_t* CO_OD_getFlagsPointer(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex);
/**
* Initialize SDO transfer.
*
* Find object in OD, verify, fill ODF_arg s.
*
* @param SDO This object.
* @param index Index of the object in Object dictionary.
* @param subIndex subIndex of the object in Object dictionary.
*
* @return 0 on success, otherwise #CO_SDO_abortCode_t.
*/
uint32_t CO_SDO_initTransfer(CO_SDO_t *SDO, uint16_t index, uint8_t subIndex);
/**
* Read data from @ref CO_SDO_objectDictionary to internal buffer.
*
* ODF_arg s must be initialized before with CO_SDO_initTransfer().
* @ref CO_SDO_OD_function is called if configured.
*
* @param SDO This object.
* @param SDOBufferSize Total size of the SDO buffer.
*
* @return 0 on success, otherwise #CO_SDO_abortCode_t.
*/
uint32_t CO_SDO_readOD(CO_SDO_t *SDO, uint16_t SDOBufferSize);
/**
* Write data from internal buffer to @ref CO_SDO_objectDictionary.
*
* ODF_arg s must be initialized before with CO_SDO_initTransfer().
* @ref CO_SDO_OD_function is called if configured.
*
* @param SDO This object.
* @param length Length of data (received from network) to write.
*
* @return 0 on success, otherwise #CO_SDO_abortCode_t.
*/
uint32_t CO_SDO_writeOD(CO_SDO_t *SDO, uint16_t length);
#ifdef __cplusplus
}
#endif /*__cplusplus*/
/** @} */
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,351 @@
/**
* CANopen Service Data Object - client protocol.
*
* @file CO_SDOmaster.h
* @ingroup CO_SDOmaster
* @author Janez Paternoster
* @author Matej Severkar
* @copyright 2004 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CO_SDO_CLIENT_H
#define CO_SDO_CLIENT_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup CO_SDOmaster SDO client
* @ingroup CO_CANopen
* @{
*
* CANopen Service Data Object - client protocol.
*
* @see @ref CO_SDO
*/
/**
* Return values of SDO client functions.
*/
typedef enum{
/** Transmit buffer is full. Waiting */
CO_SDOcli_transmittBufferFull = 4,
/** Block download is in progress. Sending train of messages */
CO_SDOcli_blockDownldInProgress = 3,
/** Block upload in progress. Receiving train of messages */
CO_SDOcli_blockUploadInProgress = 2,
/** Waiting server response */
CO_SDOcli_waitingServerResponse = 1,
/** Success, end of communication */
CO_SDOcli_ok_communicationEnd = 0,
/** Error in arguments */
CO_SDOcli_wrongArguments = -2,
/** Communication ended with client abort */
CO_SDOcli_endedWithClientAbort = -9,
/** Communication ended with server abort */
CO_SDOcli_endedWithServerAbort = -10,
/** Communication ended with timeout */
CO_SDOcli_endedWithTimeout = -11
}CO_SDOclient_return_t;
/**
* SDO Client Parameter. The same as record from Object dictionary (index 0x1280+).
*/
typedef struct{
/** Equal to 3 */
uint8_t maxSubIndex;
/** Communication object identifier for client transmission. Meaning of the specific bits:
- Bit 0...10: 11-bit CAN identifier.
- Bit 11..30: reserved, set to 0.
- Bit 31: if 1, SDO client object is not used. */
uint32_t COB_IDClientToServer;
/** Communication object identifier for message received from server. Meaning of the specific bits:
- Bit 0...10: 11-bit CAN identifier.
- Bit 11..30: reserved, set to 0.
- Bit 31: if 1, SDO client object is not used. */
uint32_t COB_IDServerToClient;
/** Node-ID of the SDO server */
uint8_t nodeIDOfTheSDOServer;
}CO_SDOclientPar_t;
/**
* SDO client object
*/
typedef struct{
/** From CO_SDOclient_init() */
CO_SDOclientPar_t *SDOClientPar;
/** From CO_SDOclient_init() */
CO_SDO_t *SDO;
/** Internal state of the SDO client */
uint8_t state;
/** Pointer to data buffer supplied by user */
uint8_t *buffer;
/** By download application indicates data size in buffer.
By upload application indicates buffer size */
uint32_t bufferSize;
/** Offset in buffer of next data segment being read/written */
uint32_t bufferOffset;
/** Acknowledgement */
uint32_t bufferOffsetACK;
/** data length to be uploaded in block transfer */
uint32_t dataSize;
/** Data length transferred in block transfer */
uint32_t dataSizeTransfered;
/** Timeout timer for SDO communication */
uint16_t timeoutTimer;
/** Timeout timer for SDO block transfer */
uint16_t timeoutTimerBLOCK;
/** Index of current object in Object Dictionary */
uint16_t index;
/** Subindex of current object in Object Dictionary */
uint8_t subIndex;
/** From CO_SDOclient_init() */
CO_CANmodule_t *CANdevRx;
/** From CO_SDOclient_init() */
uint16_t CANdevRxIdx;
/** Indicates, if new SDO message received from CAN bus.
It is not cleared, until received message is completely processed. */
volatile void *CANrxNew;
/** 8 data bytes of the received message */
uint8_t CANrxData[8];
/** From CO_SDOclient_initCallback() or NULL */
void (*pFunctSignal)(void);
/** From CO_SDOclient_init() */
CO_CANmodule_t *CANdevTx;
/** CAN transmit buffer inside CANdevTx for CAN tx message */
CO_CANtx_t *CANtxBuff;
/** From CO_SDOclient_init() */
uint16_t CANdevTxIdx;
/** Toggle bit toggled with each subsequent in segmented transfer */
uint8_t toggle;
/** Server threshold for switch back to segmented transfer, if data size is small.
Set in CO_SDOclient_init(). Can be changed by application. 0 Disables switching. */
uint8_t pst;
/** Maximum number of segments in one block. Set in CO_SDOclient_init(). Can
be changed by application to 2 .. 127. */
uint8_t block_size_max;
/** Last sector number */
uint8_t block_seqno;
/** Block size in current transfer */
uint8_t block_blksize;
/** Number of bytes in last segment that do not contain data */
uint8_t block_noData;
/** Server CRC support in block transfer */
uint8_t crcEnabled;
/** Previous value of the COB_IDClientToServer */
uint32_t COB_IDClientToServerPrev;
/** Previous value of the COB_IDServerToClient */
uint32_t COB_IDServerToClientPrev;
}CO_SDOclient_t;
/**
* Initialize SDO client object.
*
* Function must be called in the communication reset section.
*
* @param SDO_C This object will be initialized.
* @param SDO SDO server object. It is used in case, if client is accessing
* object dictionary from its own device. If NULL, it will be ignored.
* @param SDOClientPar Pointer to _SDO Client Parameter_ record from Object
* dictionary (index 0x1280+). Will be written.
* @param CANdevRx CAN device for SDO client reception.
* @param CANdevRxIdx Index of receive buffer in the above CAN device.
* @param CANdevTx CAN device for SDO client transmission.
* @param CANdevTxIdx Index of transmit buffer in the above CAN device.
*
* @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
*/
CO_ReturnError_t CO_SDOclient_init(
CO_SDOclient_t *SDO_C,
CO_SDO_t *SDO,
CO_SDOclientPar_t *SDOClientPar,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdx,
CO_CANmodule_t *CANdevTx,
uint16_t CANdevTxIdx);
/**
* Initialize SDOclientRx callback function.
*
* Function initializes optional callback function, which is called after new
* message is received from the CAN bus. Function may wake up external task,
* which processes mainline CANopen functions.
*
* @param SDOclient This object.
* @param pFunctSignal Pointer to the callback function. Not called if NULL.
*/
void CO_SDOclient_initCallback(
CO_SDOclient_t *SDOclient,
void (*pFunctSignal)(void));
/**
* Setup SDO client object.
*
* Function must be called before new SDO communication. If previous SDO
* communication was with the same node, function does not need to be called.
*
* @remark If configuring SDO client from network is required, this function
* should be set as callback for the corresponding SDO client parameter OD
* entry.
*
* @param SDO_C This object.
* @param COB_IDClientToServer See CO_SDOclientPar_t. If zero, then
* nodeIDOfTheSDOServer is used with default COB-ID.
* @param COB_IDServerToClient See CO_SDOclientPar_t. If zero, then
* nodeIDOfTheSDOServer is used with default COB-ID.
* @param nodeIDOfTheSDOServer Node-ID of the SDO server. If zero, SDO client
* object is not used. If it is the same as node-ID of this node, then data will
* be exchanged with this node (without CAN communication).
*
* @return #CO_SDOclient_return_t
*/
CO_SDOclient_return_t CO_SDOclient_setup(
CO_SDOclient_t *SDO_C,
uint32_t COB_IDClientToServer,
uint32_t COB_IDServerToClient,
uint8_t nodeIDOfTheSDOServer);
/**
* Initiate SDO download communication.
*
* Function initiates SDO download communication with server specified in
* CO_SDOclient_init() function. Data will be written to remote node.
* Function is non-blocking.
*
* @param SDO_C This object.
* @param index Index of object in object dictionary in remote node.
* @param subIndex Subindex of object in object dictionary in remote node.
* @param dataTx Pointer to data to be written. Data must be valid until end
* of communication. Note that data are aligned in little-endian
* format, because CANopen itself uses little-endian. Take care,
* when using processors with big-endian.
* @param dataSize Size of data in dataTx.
* @param blockEnable Try to initiate block transfer.
*
* @return #CO_SDOclient_return_t
*/
CO_SDOclient_return_t CO_SDOclientDownloadInitiate(
CO_SDOclient_t *SDO_C,
uint16_t index,
uint8_t subIndex,
uint8_t *dataTx,
uint32_t dataSize,
uint8_t blockEnable);
/**
* Process SDO download communication.
*
* Function must be called cyclically until it returns <=0. It Proceeds SDO
* download communication initiated with CO_SDOclientDownloadInitiate().
* Function is non-blocking.
*
* @param SDO_C This object.
* @param timeDifference_ms Time difference from previous function call in [milliseconds].
* @param SDOtimeoutTime Timeout time for SDO communication in milliseconds.
* @param pSDOabortCode Pointer to external variable written by this function
* in case of error in communication.
*
* @return #CO_SDOclient_return_t
*/
CO_SDOclient_return_t CO_SDOclientDownload(
CO_SDOclient_t *SDO_C,
uint16_t timeDifference_ms,
uint16_t SDOtimeoutTime,
uint32_t *pSDOabortCode);
/**
* Initiate SDO upload communication.
*
* Function initiates SDO upload communication with server specified in
* CO_SDOclient_init() function. Data will be read from remote node.
* Function is non-blocking.
*
* @param SDO_C This object.
* @param index Index of object in object dictionary in remote node.
* @param subIndex Subindex of object in object dictionary in remote node.
* @param dataRx Pointer to data buffer, into which received data will be written.
* Buffer must be valid until end of communication. Note that data are aligned
* in little-endian format, because CANopen itself uses
* little-endian. Take care, when using processors with big-endian.
* @param dataRxSize Size of dataRx.
* @param blockEnable Try to initiate block transfer.
*
* @return #CO_SDOclient_return_t
*/
CO_SDOclient_return_t CO_SDOclientUploadInitiate(
CO_SDOclient_t *SDO_C,
uint16_t index,
uint8_t subIndex,
uint8_t *dataRx,
uint32_t dataRxSize,
uint8_t blockEnable);
/**
* Process SDO upload communication.
*
* Function must be called cyclically until it returns <=0. It Proceeds SDO
* upload communication initiated with CO_SDOclientUploadInitiate().
* Function is non-blocking.
*
* @param SDO_C This object.
* @param timeDifference_ms Time difference from previous function call in [milliseconds].
* @param SDOtimeoutTime Timeout time for SDO communication in milliseconds.
* @param pDataSize pointer to external variable, where size of received
* data will be written.
* @param pSDOabortCode Pointer to external variable written by this function
* in case of error in communication.
*
* @return #CO_SDOclient_return_t
*/
CO_SDOclient_return_t CO_SDOclientUpload(
CO_SDOclient_t *SDO_C,
uint16_t timeDifference_ms,
uint16_t SDOtimeoutTime,
uint32_t *pDataSize,
uint32_t *pSDOabortCode);
/**
* Close SDO communication temporary.
*
* Function must be called after finish of each SDO client communication cycle.
* It disables reception of SDO client CAN messages. It is necessary, because
* CO_SDOclient_receive function may otherwise write into undefined SDO buffer.
*/
void CO_SDOclientClose(CO_SDOclient_t *SDO_C);
#ifdef __cplusplus
}
#endif /*__cplusplus*/
/** @} */
#endif

View File

@ -0,0 +1,361 @@
/*
* CANopen SYNC object.
*
* @file CO_SYNC.c
* @ingroup CO_SYNC
* @author Janez Paternoster
* @copyright 2004 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "CO_driver.h"
#include "CO_SDO.h"
#include "CO_Emergency.h"
#include "CO_NMT_Heartbeat.h"
#include "CO_SYNC.h"
/*
* Read received message from CAN module.
*
* Function will be called (by CAN receive interrupt) every time, when CAN
* message with correct identifier will be received. For more information and
* description of parameters see file CO_driver.h.
*/
static void CO_SYNC_receive(void *object, const CO_CANrxMsg_t *msg){
CO_SYNC_t *SYNC;
uint8_t operState;
SYNC = (CO_SYNC_t*)object; /* this is the correct pointer type of the first argument */
operState = *SYNC->operatingState;
if((operState == CO_NMT_OPERATIONAL) || (operState == CO_NMT_PRE_OPERATIONAL)){
if(SYNC->counterOverflowValue == 0){
if(msg->DLC == 0U){
SET_CANrxNew(SYNC->CANrxNew);
}
else{
SYNC->receiveError = (uint16_t)msg->DLC | 0x0100U;
}
}
else{
if(msg->DLC == 1U){
SYNC->counter = msg->data[0];
SET_CANrxNew(SYNC->CANrxNew);
}
else{
SYNC->receiveError = (uint16_t)msg->DLC | 0x0200U;
}
}
if(IS_CANrxNew(SYNC->CANrxNew)) {
SYNC->CANrxToggle = SYNC->CANrxToggle ? false : true;
}
}
}
/*
* Function for accessing _COB ID SYNC Message_ (index 0x1005) from SDO server.
*
* For more information see file CO_SDO.h.
*/
static CO_SDO_abortCode_t CO_ODF_1005(CO_ODF_arg_t *ODF_arg){
CO_SYNC_t *SYNC;
uint32_t value;
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
SYNC = (CO_SYNC_t*) ODF_arg->object;
value = CO_getUint32(ODF_arg->data);
if(!ODF_arg->reading){
uint8_t configureSyncProducer = 0;
/* only 11-bit CAN identifier is supported */
if(value & 0x20000000UL){
ret = CO_SDO_AB_INVALID_VALUE;
}
else{
/* is 'generate Sync messge' bit set? */
if(value & 0x40000000UL){
/* if bit was set before, value can not be changed */
if(SYNC->isProducer){
ret = CO_SDO_AB_DATA_DEV_STATE;
}
else{
configureSyncProducer = 1;
}
}
}
/* configure sync producer and consumer */
if(ret == CO_SDO_AB_NONE){
SYNC->COB_ID = (uint16_t)(value & 0x7FFU);
if(configureSyncProducer){
uint8_t len = 0U;
if(SYNC->counterOverflowValue != 0U){
len = 1U;
SYNC->counter = 0U;
SYNC->timer = 0U;
}
SYNC->CANtxBuff = CO_CANtxBufferInit(
SYNC->CANdevTx, /* CAN device */
SYNC->CANdevTxIdx, /* index of specific buffer inside CAN module */
SYNC->COB_ID, /* CAN identifier */
0, /* rtr */
len, /* number of data bytes */
0); /* synchronous message flag bit */
SYNC->isProducer = true;
}
else{
SYNC->isProducer = false;
}
CO_CANrxBufferInit(
SYNC->CANdevRx, /* CAN device */
SYNC->CANdevRxIdx, /* rx buffer index */
SYNC->COB_ID, /* CAN identifier */
0x7FF, /* mask */
0, /* rtr */
(void*)SYNC, /* object passed to receive function */
CO_SYNC_receive); /* this function will process received message */
}
}
return ret;
}
/*
* Function for accessing _Communication cycle period_ (index 0x1006) from SDO server.
*
* For more information see file CO_SDO.h.
*/
static CO_SDO_abortCode_t CO_ODF_1006(CO_ODF_arg_t *ODF_arg){
CO_SYNC_t *SYNC;
uint32_t value;
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
SYNC = (CO_SYNC_t*) ODF_arg->object;
value = CO_getUint32(ODF_arg->data);
if(!ODF_arg->reading){
/* period transition from 0 to something */
if((SYNC->periodTime == 0) && (value != 0)){
SYNC->counter = 0;
}
SYNC->periodTime = value;
SYNC->periodTimeoutTime = (value / 2U) * 3U;
/* overflow? */
if(SYNC->periodTimeoutTime < value){
SYNC->periodTimeoutTime = 0xFFFFFFFFUL;
}
SYNC->timer = 0;
}
return ret;
}
/**
* Function for accessing _Synchronous counter overflow value_ (index 0x1019) from SDO server.
*
* For more information see file CO_SDO.h.
*/
static CO_SDO_abortCode_t CO_ODF_1019(CO_ODF_arg_t *ODF_arg){
CO_SYNC_t *SYNC;
uint8_t value;
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
SYNC = (CO_SYNC_t*) ODF_arg->object;
value = ODF_arg->data[0];
if(!ODF_arg->reading){
uint8_t len = 0U;
if(SYNC->periodTime){
ret = CO_SDO_AB_DATA_DEV_STATE;
}
else if((value == 1) || (value > 240)){
ret = CO_SDO_AB_INVALID_VALUE;
}
else{
SYNC->counterOverflowValue = value;
if(value != 0){
len = 1U;
}
SYNC->CANtxBuff = CO_CANtxBufferInit(
SYNC->CANdevTx, /* CAN device */
SYNC->CANdevTxIdx, /* index of specific buffer inside CAN module */
SYNC->COB_ID, /* CAN identifier */
0, /* rtr */
len, /* number of data bytes */
0); /* synchronous message flag bit */
}
}
return ret;
}
/******************************************************************************/
CO_ReturnError_t CO_SYNC_init(
CO_SYNC_t *SYNC,
CO_EM_t *em,
CO_SDO_t *SDO,
uint8_t *operatingState,
uint32_t COB_ID_SYNCMessage,
uint32_t communicationCyclePeriod,
uint8_t synchronousCounterOverflowValue,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdx,
CO_CANmodule_t *CANdevTx,
uint16_t CANdevTxIdx)
{
uint8_t len = 0;
/* verify arguments */
if(SYNC==NULL || em==NULL || SDO==NULL || operatingState==NULL ||
CANdevRx==NULL || CANdevTx==NULL){
return CO_ERROR_ILLEGAL_ARGUMENT;
}
/* Configure object variables */
SYNC->isProducer = (COB_ID_SYNCMessage&0x40000000L) ? true : false;
SYNC->COB_ID = COB_ID_SYNCMessage&0x7FF;
SYNC->periodTime = communicationCyclePeriod;
SYNC->periodTimeoutTime = communicationCyclePeriod / 2 * 3;
/* overflow? */
if(SYNC->periodTimeoutTime < communicationCyclePeriod) SYNC->periodTimeoutTime = 0xFFFFFFFFL;
SYNC->counterOverflowValue = synchronousCounterOverflowValue;
if(synchronousCounterOverflowValue) len = 1;
SYNC->curentSyncTimeIsInsideWindow = true;
CLEAR_CANrxNew(SYNC->CANrxNew);
SYNC->CANrxToggle = false;
SYNC->timer = 0;
SYNC->counter = 0;
SYNC->receiveError = 0U;
SYNC->em = em;
SYNC->operatingState = operatingState;
SYNC->CANdevRx = CANdevRx;
SYNC->CANdevRxIdx = CANdevRxIdx;
/* Configure Object dictionary entry at index 0x1005, 0x1006 and 0x1019 */
CO_OD_configure(SDO, OD_H1005_COBID_SYNC, CO_ODF_1005, (void*)SYNC, 0, 0);
CO_OD_configure(SDO, OD_H1006_COMM_CYCL_PERIOD, CO_ODF_1006, (void*)SYNC, 0, 0);
CO_OD_configure(SDO, OD_H1019_SYNC_CNT_OVERFLOW, CO_ODF_1019, (void*)SYNC, 0, 0);
/* configure SYNC CAN reception */
CO_CANrxBufferInit(
CANdevRx, /* CAN device */
CANdevRxIdx, /* rx buffer index */
SYNC->COB_ID, /* CAN identifier */
0x7FF, /* mask */
0, /* rtr */
(void*)SYNC, /* object passed to receive function */
CO_SYNC_receive); /* this function will process received message */
/* configure SYNC CAN transmission */
SYNC->CANdevTx = CANdevTx;
SYNC->CANdevTxIdx = CANdevTxIdx;
SYNC->CANtxBuff = CO_CANtxBufferInit(
CANdevTx, /* CAN device */
CANdevTxIdx, /* index of specific buffer inside CAN module */
SYNC->COB_ID, /* CAN identifier */
0, /* rtr */
len, /* number of data bytes */
0); /* synchronous message flag bit */
return CO_ERROR_NO;
}
/******************************************************************************/
uint8_t CO_SYNC_process(
CO_SYNC_t *SYNC,
uint32_t timeDifference_us,
uint32_t ObjDict_synchronousWindowLength)
{
uint8_t ret = 0;
uint32_t timerNew;
if(*SYNC->operatingState == CO_NMT_OPERATIONAL || *SYNC->operatingState == CO_NMT_PRE_OPERATIONAL){
/* update sync timer, no overflow */
timerNew = SYNC->timer + timeDifference_us;
if(timerNew > SYNC->timer) SYNC->timer = timerNew;
/* was SYNC just received */
if(IS_CANrxNew(SYNC->CANrxNew)){
SYNC->timer = 0;
ret = 1;
CLEAR_CANrxNew(SYNC->CANrxNew);
}
/* SYNC producer */
if(SYNC->isProducer && SYNC->periodTime){
if(SYNC->timer >= SYNC->periodTime){
if(++SYNC->counter > SYNC->counterOverflowValue) SYNC->counter = 1;
SYNC->timer = 0;
ret = 1;
SYNC->CANrxToggle = SYNC->CANrxToggle ? false : true;
SYNC->CANtxBuff->data[0] = SYNC->counter;
CO_CANsend(SYNC->CANdevTx, SYNC->CANtxBuff);
}
}
/* Synchronous PDOs are allowed only inside time window */
if(ObjDict_synchronousWindowLength){
if(SYNC->timer > ObjDict_synchronousWindowLength){
if(SYNC->curentSyncTimeIsInsideWindow){
ret = 2;
}
SYNC->curentSyncTimeIsInsideWindow = false;
}
else{
SYNC->curentSyncTimeIsInsideWindow = true;
}
}
else{
SYNC->curentSyncTimeIsInsideWindow = true;
}
/* Verify timeout of SYNC */
if(SYNC->periodTime && SYNC->timer > SYNC->periodTimeoutTime && *SYNC->operatingState == CO_NMT_OPERATIONAL)
CO_errorReport(SYNC->em, CO_EM_SYNC_TIME_OUT, CO_EMC_COMMUNICATION, SYNC->timer);
}
else {
CLEAR_CANrxNew(SYNC->CANrxNew);
}
/* verify error from receive function */
if(SYNC->receiveError != 0U){
CO_errorReport(SYNC->em, CO_EM_SYNC_LENGTH, CO_EMC_SYNC_DATA_LENGTH, (uint32_t)SYNC->receiveError);
SYNC->receiveError = 0U;
}
return ret;
}

View File

@ -0,0 +1,164 @@
/**
* CANopen SYNC object protocol.
*
* @file CO_SYNC.h
* @ingroup CO_SYNC
* @author Janez Paternoster
* @copyright 2004 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CO_SYNC_H
#define CO_SYNC_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup CO_SYNC SYNC
* @ingroup CO_CANopen
* @{
*
* CANopen SYNC object protocol.
*
* For CAN identifier see #CO_Default_CAN_ID_t
*
* SYNC message is used for synchronization of the nodes on network. One node
* can be SYNC producer, others can be SYNC consumers. Synchronous TPDOs are
* transmitted after the CANopen SYNC message. Synchronous received PDOs are
* accepted(copied to OD) immediatelly after the reception of the next SYNC
* message.
*
* ####Contents of SYNC message
* By default SYNC message has no data. If _Synchronous counter overflow value_
* from Object dictionary (index 0x1019) is different than 0, SYNC message has
* one data byte: _counter_ incremented by 1 with every SYNC transmission.
*
* ####SYNC in CANopenNode
* According to CANopen, synchronous RPDOs must be processed after reception of
* the next sync messsage. For that reason, there is a double receive buffer
* for each synchronous RPDO. At the moment, when SYNC is received or
* transmitted, internal variable CANrxToggle toggles. That variable is then
* used by synchronous RPDO to determine, which of the two buffers is used for
* RPDO reception and which for RPDO processing.
*/
/**
* SYNC producer and consumer object.
*/
typedef struct{
CO_EM_t *em; /**< From CO_SYNC_init() */
uint8_t *operatingState; /**< From CO_SYNC_init() */
/** True, if device is SYNC producer. Calculated from _COB ID SYNC Message_
variable from Object dictionary (index 0x1005). */
bool_t isProducer;
/** COB_ID of SYNC message. Calculated from _COB ID SYNC Message_
variable from Object dictionary (index 0x1005). */
uint16_t COB_ID;
/** Sync period time in [microseconds]. Calculated from _Communication cycle period_
variable from Object dictionary (index 0x1006). */
uint32_t periodTime;
/** Sync period timeout time in [microseconds].
(periodTimeoutTime = periodTime * 1,5) */
uint32_t periodTimeoutTime;
/** Value from _Synchronous counter overflow value_ variable from Object
dictionary (index 0x1019) */
uint8_t counterOverflowValue;
/** True, if current time is inside synchronous window.
In this case synchronous PDO may be sent. */
bool_t curentSyncTimeIsInsideWindow;
/** Indicates, if new SYNC message received from CAN bus */
volatile void *CANrxNew;
/** Variable toggles, if new SYNC message received from CAN bus */
bool_t CANrxToggle;
/** Counter of the SYNC message if counterOverflowValue is different than zero */
uint8_t counter;
/** Timer for the SYNC message in [microseconds].
Set to zero after received or transmitted SYNC message */
uint32_t timer;
/** Set to nonzero value, if SYNC with wrong data length is received from CAN */
uint16_t receiveError;
CO_CANmodule_t *CANdevRx; /**< From CO_SYNC_init() */
uint16_t CANdevRxIdx; /**< From CO_SYNC_init() */
CO_CANmodule_t *CANdevTx; /**< From CO_SYNC_init() */
CO_CANtx_t *CANtxBuff; /**< CAN transmit buffer inside CANdevTx */
uint16_t CANdevTxIdx; /**< From CO_SYNC_init() */
}CO_SYNC_t;
/**
* Initialize SYNC object.
*
* Function must be called in the communication reset section.
*
* @param SYNC This object will be initialized.
* @param em Emergency object.
* @param SDO SDO server object.
* @param operatingState Pointer to variable indicating CANopen device NMT internal state.
* @param COB_ID_SYNCMessage From Object dictionary (index 0x1005).
* @param communicationCyclePeriod From Object dictionary (index 0x1006).
* @param synchronousCounterOverflowValue From Object dictionary (index 0x1019).
* @param CANdevRx CAN device for SYNC reception.
* @param CANdevRxIdx Index of receive buffer in the above CAN device.
* @param CANdevTx CAN device for SYNC transmission.
* @param CANdevTxIdx Index of transmit buffer in the above CAN device.
*
* @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
*/
CO_ReturnError_t CO_SYNC_init(
CO_SYNC_t *SYNC,
CO_EM_t *em,
CO_SDO_t *SDO,
uint8_t *operatingState,
uint32_t COB_ID_SYNCMessage,
uint32_t communicationCyclePeriod,
uint8_t synchronousCounterOverflowValue,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdx,
CO_CANmodule_t *CANdevTx,
uint16_t CANdevTxIdx);
/**
* Process SYNC communication.
*
* Function must be called cyclically.
*
* @param SYNC This object.
* @param timeDifference_us Time difference from previous function call in [microseconds].
* @param ObjDict_synchronousWindowLength _Synchronous window length_ variable from
* Object dictionary (index 0x1007).
*
* @return 0: No special meaning.
* @return 1: New SYNC message recently received or was just transmitted.
* @return 2: SYNC time was just passed out of window.
*/
uint8_t CO_SYNC_process(
CO_SYNC_t *SYNC,
uint32_t timeDifference_us,
uint32_t ObjDict_synchronousWindowLength);
#ifdef __cplusplus
}
#endif /*__cplusplus*/
/** @} */
#endif

View File

@ -0,0 +1,165 @@
/*
* CANopen TIME object.
*
* @file CO_TIME.c
* @ingroup CO_TIME
* @author Julien PEYREGNE
* @copyright 2019 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "CANopen.h"
/*
* Read received message from CAN module.
*
* Function will be called (by CAN receive interrupt) every time, when CAN
* message with correct identifier will be received.
*/
static void CO_TIME_receive(void *object, const CO_CANrxMsg_t *msg){
CO_TIME_t *TIME;
uint8_t operState;
TIME = (CO_TIME_t*)object; /* this is the correct pointer type of the first argument */
operState = *TIME->operatingState;
if((operState == CO_NMT_OPERATIONAL) || (operState == CO_NMT_PRE_OPERATIONAL)){
SET_CANrxNew(TIME->CANrxNew);
// Process Time from msg buffer
CO_memcpy((uint8_t*)&TIME->Time.ullValue, msg->data, msg->DLC);
}
else{
TIME->receiveError = (uint16_t)msg->DLC;
}
}
/******************************************************************************/
CO_ReturnError_t CO_TIME_init(
CO_TIME_t *TIME,
CO_EM_t *em,
CO_SDO_t *SDO,
uint8_t *operatingState,
uint32_t COB_ID_TIMEMessage,
uint32_t TIMECyclePeriod,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdx,
CO_CANmodule_t *CANdevTx,
uint16_t CANdevTxIdx)
{
/* verify arguments */
if(TIME==NULL || em==NULL || SDO==NULL || operatingState==NULL ||
CANdevRx==NULL || CANdevTx==NULL){
return CO_ERROR_ILLEGAL_ARGUMENT;
}
/* Configure object variables */
TIME->isConsumer = (COB_ID_TIMEMessage&0x80000000L) ? true : false;
TIME->isProducer = (COB_ID_TIMEMessage&0x40000000L) ? true : false;
TIME->COB_ID = COB_ID_TIMEMessage&0x7FF; // 11 bit ID
TIME->periodTime = TIMECyclePeriod;
TIME->periodTimeoutTime = TIMECyclePeriod / 2 * 3;
/* overflow? */
if(TIME->periodTimeoutTime < TIMECyclePeriod)
TIME->periodTimeoutTime = 0xFFFFFFFFL;
CLEAR_CANrxNew(TIME->CANrxNew);
TIME->timer = 0;
TIME->receiveError = 0U;
TIME->em = em;
TIME->operatingState = operatingState;
/* configure TIME consumer message reception */
TIME->CANdevRx = CANdevRx;
TIME->CANdevRxIdx = CANdevRxIdx;
if(TIME->isConsumer)
CO_CANrxBufferInit(
CANdevRx, /* CAN device */
CANdevRxIdx, /* rx buffer index */
TIME->COB_ID, /* CAN identifier */
0x7FF, /* mask */
0, /* rtr */
(void*)TIME, /* object passed to receive function */
CO_TIME_receive); /* this function will process received message */
/* configure TIME producer message transmission */
TIME->CANdevTx = CANdevTx;
TIME->CANdevTxIdx = CANdevTxIdx;
if(TIME->isProducer)
TIME->TXbuff = CO_CANtxBufferInit(
CANdevTx, /* CAN device */
CANdevTxIdx, /* index of specific buffer inside CAN module */
TIME->COB_ID, /* CAN identifier */
0, /* rtr */
TIME_MSG_LENGTH, /* number of data bytes */
0); /* synchronous message flag bit */
return CO_ERROR_NO;
}
/******************************************************************************/
uint8_t CO_TIME_process(
CO_TIME_t *TIME,
uint32_t timeDifference_ms)
{
uint8_t ret = 0;
uint32_t timerNew;
if(*TIME->operatingState == CO_NMT_OPERATIONAL || *TIME->operatingState == CO_NMT_PRE_OPERATIONAL){
/* update TIME timer, no overflow */
timerNew = TIME->timer + timeDifference_ms;
if(timerNew > TIME->timer)
TIME->timer = timerNew;
/* was TIME just received */
if(TIME->CANrxNew){
TIME->timer = 0;
ret = 1;
CLEAR_CANrxNew(TIME->CANrxNew);
}
/* TIME producer */
if(TIME->isProducer && TIME->periodTime){
if(TIME->timer >= TIME->periodTime){
TIME->timer = 0;
ret = 1;
CO_memcpy(TIME->TXbuff->data, (const uint8_t*)&TIME->Time.ullValue, TIME_MSG_LENGTH);
CO_CANsend(TIME->CANdevTx, TIME->TXbuff);
}
}
/* Verify TIME timeout if node is consumer */
if(TIME->isConsumer && TIME->periodTime && TIME->timer > TIME->periodTimeoutTime
&& *TIME->operatingState == CO_NMT_OPERATIONAL)
CO_errorReport(TIME->em, CO_EM_TIME_TIMEOUT, CO_EMC_COMMUNICATION, TIME->timer);
}
else {
CLEAR_CANrxNew(TIME->CANrxNew);
}
/* verify error from receive function */
if(TIME->receiveError != 0U){
CO_errorReport(TIME->em, CO_EM_TIME_LENGTH, CO_EMC_TIME_DATA_LENGTH, (uint32_t)TIME->receiveError);
TIME->receiveError = 0U;
}
return ret;
}

View File

@ -0,0 +1,158 @@
/**
* CANopen TIME object protocol.
*
* @file CO_TIME.c
* @ingroup CO_TIME
* @author Julien PEYREGNE
* @copyright 2019 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CO_TIME_H
#define CO_TIME_H
#include "CO_OD.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup CO_TIME TIME
* @ingroup CO_CANopen
* @{
*
* CANopen TIME object protocol.
*
* For CAN identifier see #CO_Default_CAN_ID_t
*
* TIME message is used for time synchronization of the nodes on network. One node
* should be TIME producer, others can be TIME consumers. This is configured with
* COB_ID_TIME object 0x1012 :
*
* - bit 31 should be set for a consumer
* - bit 30 should be set for a producer
*
*
* ###TIME CONSUMER
*
* CO_TIME_init() configuration :
* - COB_ID_TIME : 0x80000100L -> TIME consumer with TIME_COB_ID = 0x100
* - TIMECyclePeriod :
* - 0 -> no EMCY will be transmitted in case of TIME timeout
* - X -> an EMCY will be transmitted in case of TIME timeout (X * 1.5) ms
*
* Latest time value is stored in \p CO->TIME->Time variable.
*
*
* ###TIME PRODUCER
*
* CO_TIME_init() configuration :
* - COB_ID_TIME : 0x40000100L -> TIME producer with TIME_COB_ID = 0x100
* - TIMECyclePeriod : Time transmit period in ms
*
* Write time value in \p CO->TIME->Time variable, this will be sent at TIMECyclePeriod.
*/
#define TIME_MSG_LENGTH 6U
/**
* TIME producer and consumer object.
*/
typedef struct{
CO_EM_t *em; /**< From CO_TIME_init() */
uint8_t *operatingState; /**< From CO_TIME_init() */
/** True, if device is TIME consumer. Calculated from _COB ID TIME Message_
variable from Object dictionary (index 0x1012). */
bool_t isConsumer;
/** True, if device is TIME producer. Calculated from _COB ID TIME Message_
variable from Object dictionary (index 0x1012). */
bool_t isProducer;
uint16_t COB_ID; /**< From CO_TIME_init() */
/** TIME period time in [milliseconds]. Set to TIME period to enable
timeout detection */
uint32_t periodTime;
/** TIME period timeout time in [milliseconds].
(periodTimeoutTime = periodTime * 1,5) */
uint32_t periodTimeoutTime;
/** Variable indicates, if new TIME message received from CAN bus */
volatile void *CANrxNew;
/** Timer for the TIME message in [microseconds].
Set to zero after received or transmitted TIME message */
uint32_t timer;
/** Set to nonzero value, if TIME with wrong data length is received from CAN */
uint16_t receiveError;
CO_CANmodule_t *CANdevRx; /**< From CO_TIME_init() */
uint16_t CANdevRxIdx; /**< From CO_TIME_init() */
CO_CANmodule_t *CANdevTx; /**< From CO_TIME_init() */
uint16_t CANdevTxIdx; /**< From CO_TIME_init() */
CO_CANtx_t *TXbuff; /**< CAN transmit buffer */
TIME_OF_DAY Time;
}CO_TIME_t;
/**
* Initialize TIME object.
*
* Function must be called in the communication reset section.
*
* @param TIME This object will be initialized.
* @param em Emergency object.
* @param SDO SDO server object.
* @param operatingState Pointer to variable indicating CANopen device NMT internal state.
* @param COB_ID_TIMEMessage Should be intialized with CO_CAN_ID_TIME_STAMP
* @param TIMECyclePeriod TIME period in ms (may also be used in consumer mode for timeout detection (1.5x period)).
* @param CANdevRx CAN device for TIME reception.
* @param CANdevRxIdx Index of receive buffer in the above CAN device.
*
* @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
*/
CO_ReturnError_t CO_TIME_init(
CO_TIME_t *TIME,
CO_EM_t *em,
CO_SDO_t *SDO,
uint8_t *operatingState,
uint32_t COB_ID_TIMEMessage,
uint32_t TIMECyclePeriod,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdx,
CO_CANmodule_t *CANdevTx,
uint16_t CANdevTxIdx);
/**
* Process TIME communication.
*
* Function must be called cyclically.
*
* @param TIME This object.
* @param timeDifference_ms Time difference from previous function call in [milliseconds].
*
* @return 0: No special meaning.
* @return 1: New TIME message recently received (consumer) / transmited (producer).
*/
uint8_t CO_TIME_process(
CO_TIME_t *TIME,
uint32_t timeDifference_ms);
#ifdef __cplusplus
}
#endif /*__cplusplus*/
/** @} */
#endif

View File

@ -0,0 +1,281 @@
/**
* CAN driver functions.
*
* Defines functions that must be implemented for each target.
*
* @file CO_driver.h
* @ingroup CO_driver
* @author Janez Paternoster
* @copyright 2004 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CO_DRIVER_H
#define CO_DRIVER_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include "CO_driver_target.h"
#include "CO_types.h"
/* Include processor header file */
#include <stdint.h> /* for 'int8_t' to 'uint64_t' */
/**
* @defgroup CO_driver Driver
* @ingroup CO_CANopen
* @{
*
* Microcontroller specific code for CANopenNode.
*
* This file contains type definitions, functions and macros for:
* - Basic data types.
* - Receive and transmit buffers for CANopen messages.
* - Interaction with CAN module on the microcontroller.
* - CAN receive and transmit interrupts.
*
* This file is not only a CAN driver. There are no classic CAN queues for CAN
* messages. This file provides direct connection with other CANopen
* objects. It tries to provide fast responses and tries to avoid unnecessary
* calculations and memory consumptions.
*
* CO_CANmodule_t contains an array of _Received message objects_ (of type
* CO_CANrx_t) and an array of _Transmit message objects_ (of type CO_CANtx_t).
* Each CANopen communication object owns one member in one of the arrays.
* For example Heartbeat producer generates one CANopen transmitting object,
* so it has reserved one member in CO_CANtx_t array.
* SYNC module may produce sync or consume sync, so it has reserved one member
* in CO_CANtx_t and one member in CO_CANrx_t array.
*
* ###Reception of CAN messages.
* Before CAN messages can be received, each member in CO_CANrx_t must be
* initialized. CO_CANrxBufferInit() is called by CANopen module, which
* uses specific member. For example @ref CO_HBconsumer uses multiple members
* in CO_CANrx_t array. (It monitors multiple heartbeat messages from remote
* nodes.) It must call CO_CANrxBufferInit() multiple times.
*
* Main arguments to the CO_CANrxBufferInit() function are CAN identifier
* and a pointer to callback function. Those two arguments (and some others)
* are copied to the member of the CO_CANrx_t array.
*
* Callback function is a function, specified by specific CANopen module
* (for example by @ref CO_HBconsumer). Each CANopen module defines own
* callback function. Callback function will process the received CAN message.
* It will copy the necessary data from CAN message to proper place. It may
* also trigger additional task, which will further process the received message.
* Callback function must be fast and must only make the necessary calculations
* and copying.
*
* Received CAN messages are processed by CAN receive interrupt function.
* After CAN message is received, function first tries to find matching CAN
* identifier from CO_CANrx_t array. If found, then a corresponding callback
* function is called.
*
* Callback function accepts two parameters:
* - object is pointer to object registered by CO_CANrxBufferInit().
* - msg is pointer to CAN message of type CO_CANrxMsg_t.
*
* Callback function must return #CO_ReturnError_t: CO_ERROR_NO,
* CO_ERROR_RX_OVERFLOW, CO_ERROR_RX_PDO_OVERFLOW, CO_ERROR_RX_MSG_LENGTH or
* CO_ERROR_RX_PDO_LENGTH.
*
*
* ###Transmission of CAN messages.
* Before CAN messages can be transmitted, each member in CO_CANtx_t must be
* initialized. CO_CANtxBufferInit() is called by CANopen module, which
* uses specific member. For example Heartbeat producer must initialize it's
* member in CO_CANtx_t array.
*
* CO_CANtxBufferInit() returns a pointer of type CO_CANtx_t, which contains buffer
* where CAN message data can be written. CAN message is send with calling
* CO_CANsend() function. If at that moment CAN transmit buffer inside
* microcontroller's CAN module is free, message is copied directly to CAN module.
* Otherwise CO_CANsend() function sets _bufferFull_ flag to true. Message will be
* then sent by CAN TX interrupt as soon as CAN module is freed. Until message is
* not copied to CAN module, its contents must not change. There may be multiple
* _bufferFull_ flags in CO_CANtx_t array set to true. In that case messages with
* lower index inside array will be sent first.
*/
/**
* Request CAN configuration (stopped) mode and *wait* untill it is set.
*
* @param CANdriverState User-provided CAN module structure.
*/
void CO_CANsetConfigurationMode(void *CANdriverState);
/**
* Request CAN normal (opearational) mode and *wait* untill it is set.
*
* @param CANmodule This object.
*/
void CO_CANsetNormalMode(CO_CANmodule_t *CANmodule);
/**
* Initialize CAN module object.
*
* Function must be called in the communication reset section. CAN module must
* be in Configuration Mode before.
*
* @param CANmodule This object will be initialized.
* @param CANdriverState User-provided CAN module structure..
* @param rxArray Array for handling received CAN messages
* @param rxSize Size of the above array. Must be equal to number of receiving CAN objects.
* @param txArray Array for handling transmitting CAN messages
* @param txSize Size of the above array. Must be equal to number of transmitting CAN objects.
* @param CANbitRate Valid values are (in kbps): 10, 20, 50, 125, 250, 500, 800, 1000.
* If value is illegal, bitrate defaults to 125.
*
* Return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
*/
CO_ReturnError_t CO_CANmodule_init(
CO_CANmodule_t *CANmodule,
void *CANdriverState,
CO_CANrx_t rxArray[],
uint16_t rxSize,
CO_CANtx_t txArray[],
uint16_t txSize,
uint16_t CANbitRate);
/**
* Switch off CANmodule. Call at program exit.
*
* @param CANmodule CAN module object.
*/
void CO_CANmodule_disable(CO_CANmodule_t *CANmodule);
/**
* Read CAN identifier from received message
*
* @param rxMsg Pointer to received message
* @return 11-bit CAN standard identifier.
*/
uint16_t CO_CANrxMsg_readIdent(const CO_CANrxMsg_t *rxMsg);
/**
* Configure CAN message receive buffer.
*
* Function configures specific CAN receive buffer. It sets CAN identifier
* and connects buffer with specific object. Function must be called for each
* member in _rxArray_ from CO_CANmodule_t.
*
* @param CANmodule This object.
* @param index Index of the specific buffer in _rxArray_.
* @param ident 11-bit standard CAN Identifier.
* @param mask 11-bit mask for identifier. Most usually set to 0x7FF.
* Received message (rcvMsg) will be accepted if the following
* condition is true: (((rcvMsgId ^ ident) & mask) == 0).
* @param rtr If true, 'Remote Transmit Request' messages will be accepted.
* @param object CANopen object, to which buffer is connected. It will be used as
* an argument to pFunct. Its type is (void), pFunct will change its
* type back to the correct object type.
* @param pFunct Pointer to function, which will be called, if received CAN
* message matches the identifier. It must be fast function.
*
* Return #CO_ReturnError_t: CO_ERROR_NO CO_ERROR_ILLEGAL_ARGUMENT or
* CO_ERROR_OUT_OF_MEMORY (not enough masks for configuration).
*/
CO_ReturnError_t CO_CANrxBufferInit(
CO_CANmodule_t *CANmodule,
uint16_t index,
uint16_t ident,
uint16_t mask,
bool_t rtr,
void *object,
void (*pFunct)(void *object, const CO_CANrxMsg_t *message));
/**
* Configure CAN message transmit buffer.
*
* Function configures specific CAN transmit buffer. Function must be called for
* each member in _txArray_ from CO_CANmodule_t.
*
* @param CANmodule This object.
* @param index Index of the specific buffer in _txArray_.
* @param ident 11-bit standard CAN Identifier.
* @param rtr If true, 'Remote Transmit Request' messages will be transmitted.
* @param noOfBytes Length of CAN message in bytes (0 to 8 bytes).
* @param syncFlag This flag bit is used for synchronous TPDO messages. If it is set,
* message will not be sent, if curent time is outside synchronous window.
*
* @return Pointer to CAN transmit message buffer. 8 bytes data array inside
* buffer should be written, before CO_CANsend() function is called.
* Zero is returned in case of wrong arguments.
*/
CO_CANtx_t *CO_CANtxBufferInit(
CO_CANmodule_t *CANmodule,
uint16_t index,
uint16_t ident,
bool_t rtr,
uint8_t noOfBytes,
bool_t syncFlag);
/**
* Send CAN message.
*
* @param CANmodule This object.
* @param buffer Pointer to transmit buffer, returned by CO_CANtxBufferInit().
* Data bytes must be written in buffer before function call.
*
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_TX_OVERFLOW or
* CO_ERROR_TX_PDO_WINDOW (Synchronous TPDO is outside window).
*/
CO_ReturnError_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer);
/**
* Clear all synchronous TPDOs from CAN module transmit buffers.
*
* CANopen allows synchronous PDO communication only inside time between SYNC
* message and SYNC Window. If time is outside this window, new synchronous PDOs
* must not be sent and all pending sync TPDOs, which may be on CAN TX buffers,
* must be cleared.
*
* This function checks (and aborts transmission if necessary) CAN TX buffers
* when it is called. Function should be called by the stack in the moment,
* when SYNC time was just passed out of synchronous window.
*
* @param CANmodule This object.
*/
void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule);
/**
* Verify all errors of CAN module.
*
* Function is called directly from CO_EM_process() function.
*
* @param CANmodule This object.
*/
void CO_CANverifyErrors(CO_CANmodule_t *CANmodule);
#ifdef __cplusplus
}
#endif /* __cplusplus */
/** @} */
#endif /* CO_DRIVER_H */

View File

@ -0,0 +1,507 @@
/*
* CANopen trace interface.
*
* @file CO_trace.c
* @author Janez Paternoster
* @copyright 2016 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "CO_trace.h"
#if CO_NO_TRACE > 0
#include <stdio.h>
#include <inttypes.h>
/* Different functions for processing value for different data types. */
static int32_t getValueI8 (void *OD_variable) { return (int32_t) *((int8_t*) OD_variable);}
static int32_t getValueI16(void *OD_variable) { return (int32_t) *((int16_t*) OD_variable);}
static int32_t getValueI32(void *OD_variable) { return *((int32_t*) OD_variable);}
static int32_t getValueU8 (void *OD_variable) { return (int32_t) *((uint8_t*) OD_variable);}
static int32_t getValueU16(void *OD_variable) { return (int32_t) *((uint16_t*) OD_variable);}
static int32_t getValueU32(void *OD_variable) { return *((int32_t*) OD_variable);}
/* Different functions for printing points for different data types. */
static uint32_t printPointCsv(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
return snprintf(s, size, "%" PRIu32 ";%" PRId32 "\n", timeStamp, value);
}
static uint32_t printPointCsvUnsigned(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
return snprintf(s, size, "%" PRIu32 ";%" PRIu32 "\n", timeStamp, (uint32_t) value);
}
static uint32_t printPointBinary(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
if(size < 8) return 0;
CO_memcpySwap4(s, &timeStamp);
CO_memcpySwap4(s+4, &value);
return 8;
}
static uint32_t printPointSvgStart(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
return snprintf(s, size, "M%" PRIu32 ",%" PRId32, timeStamp, value);
}
static uint32_t printPointSvgStartUnsigned(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
return snprintf(s, size, "M%" PRIu32 ",%" PRIu32, timeStamp, (uint32_t) value);
}
static uint32_t printPointSvg(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
return snprintf(s, size, "H%" PRIu32 "V%" PRId32, timeStamp, value);
}
static uint32_t printPointSvgUnsigned(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
return snprintf(s, size, "H%" PRIu32 "V%" PRIu32, timeStamp, (uint32_t) value);
}
/* Collection of function pointers for fast processing based on specific data type. */
/* Rules for the array: There must be groups of six members (I8, I16, I32, U8, U16, U32)
* in correct order and sequence, so findVariable() finds correct member. */
static const CO_trace_dataType_t dataTypes[] = {
{getValueI8, printPointCsv, printPointCsv, printPointCsv},
{getValueI16, printPointCsv, printPointCsv, printPointCsv},
{getValueI32, printPointCsv, printPointCsv, printPointCsv},
{getValueU8, printPointCsvUnsigned, printPointCsvUnsigned, printPointCsvUnsigned},
{getValueU16, printPointCsvUnsigned, printPointCsvUnsigned, printPointCsvUnsigned},
{getValueU32, printPointCsvUnsigned, printPointCsvUnsigned, printPointCsvUnsigned},
{getValueI8, printPointBinary, printPointBinary, printPointBinary},
{getValueI16, printPointBinary, printPointBinary, printPointBinary},
{getValueI32, printPointBinary, printPointBinary, printPointBinary},
{getValueU8, printPointBinary, printPointBinary, printPointBinary},
{getValueU16, printPointBinary, printPointBinary, printPointBinary},
{getValueU32, printPointBinary, printPointBinary, printPointBinary},
{getValueI8, printPointSvgStart, printPointSvg, printPointSvg},
{getValueI16, printPointSvgStart, printPointSvg, printPointSvg},
{getValueI32, printPointSvgStart, printPointSvg, printPointSvg},
{getValueU8, printPointSvgStartUnsigned, printPointSvgUnsigned, printPointSvgUnsigned},
{getValueU16, printPointSvgStartUnsigned, printPointSvgUnsigned, printPointSvgUnsigned},
{getValueU32, printPointSvgStartUnsigned, printPointSvgUnsigned, printPointSvgUnsigned}
};
/* Find variable in Object Dictionary *****************************************/
static void findVariable(CO_trace_t *trace) {
bool_t err = false;
uint16_t index;
uint8_t subIndex;
uint8_t dataLen;
void *OdDataPtr = NULL;
unsigned dtIndex = 0;
/* parse mapping */
index = (uint16_t) ((*trace->map) >> 16);
subIndex = (uint8_t) ((*trace->map) >> 8);
dataLen = (uint8_t) (*trace->map);
if((dataLen & 0x07) != 0) { /* data length must be byte aligned */
err = true;
}
dataLen >>= 3; /* in bytes now */
if(dataLen == 0) {
dataLen = 4;
}
/* find mapped variable, if map available */
if(!err && (index != 0 || subIndex != 0)) {
uint16_t entryNo = CO_OD_find(trace->SDO, index);
if(index >= 0x1000 && entryNo != 0xFFFF && subIndex <= trace->SDO->OD[entryNo].maxSubIndex) {
OdDataPtr = CO_OD_getDataPointer(trace->SDO, entryNo, subIndex);
}
if(OdDataPtr != NULL) {
uint16_t len = CO_OD_getLength(trace->SDO, entryNo, subIndex);
if(len < dataLen) {
dataLen = len;
}
}
else {
err = true;
}
}
/* Get function pointers for correct data type */
if(!err) {
/* first sequence: data length */
switch(dataLen) {
case 1: dtIndex = 0; break;
case 2: dtIndex = 1; break;
case 4: dtIndex = 2; break;
default: err = true; break;
}
/* second sequence: signed or unsigned */
if(((*trace->format) & 1) == 1) {
dtIndex += 3;
}
/* third sequence: Output type */
dtIndex += ((*trace->format) >> 1) * 6;
if(dtIndex > (sizeof(dataTypes) / sizeof(CO_trace_dataType_t))) {
err = true;
}
}
/* set output variables */
if(!err) {
if(OdDataPtr != NULL) {
trace->OD_variable = OdDataPtr;
}
else {
trace->OD_variable = trace->value;
}
trace->dt = &dataTypes[dtIndex];
}
else {
trace->OD_variable = NULL;
trace->dt = NULL;
}
}
/* OD function for accessing _OD_traceConfig_ (index 0x2300+) from SDO server.
* For more information see file CO_SDO.h. */
static CO_SDO_abortCode_t CO_ODF_traceConfig(CO_ODF_arg_t *ODF_arg) {
CO_trace_t *trace;
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
trace = (CO_trace_t*) ODF_arg->object;
switch(ODF_arg->subIndex) {
case 1: /* size */
if(ODF_arg->reading) {
uint32_t *value = (uint32_t*) ODF_arg->data;
*value = trace->bufferSize;
}
break;
case 2: /* axisNo (trace enabled if nonzero) */
if(ODF_arg->reading) {
uint8_t *value = (uint8_t*) ODF_arg->data;
if(!trace->enabled) {
*value = 0;
}
}
else {
uint8_t *value = (uint8_t*) ODF_arg->data;
if(*value == 0) {
trace->enabled = false;
}
else if(!trace->enabled) {
if(trace->bufferSize == 0) {
ret = CO_SDO_AB_OUT_OF_MEM;
}
else {
/* set trace->OD_variable and trace->dt, based on 'map' and 'format' */
findVariable(trace);
if(trace->OD_variable != NULL) {
*trace->value = 0;
*trace->minValue = 0;
*trace->maxValue = 0;
*trace->triggerTime = 0;
trace->valuePrev = 0;
trace->readPtr = 0;
trace->writePtr = 0;
trace->enabled = true;
}
else {
ret = CO_SDO_AB_NO_MAP;
}
}
}
}
break;
case 5: /* map */
case 6: /* format */
if(!ODF_arg->reading) {
if(trace->enabled) {
ret = CO_SDO_AB_INVALID_VALUE;
}
}
break;
}
return ret;
}
/* OD function for accessing _OD_trace_ (index 0x2400+) from SDO server.
* For more information see file CO_SDO.h. */
static CO_SDO_abortCode_t CO_ODF_trace(CO_ODF_arg_t *ODF_arg) {
CO_trace_t *trace;
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
trace = (CO_trace_t*) ODF_arg->object;
switch(ODF_arg->subIndex) {
case 1: /* size */
if(ODF_arg->reading) {
uint32_t *value = (uint32_t*) ODF_arg->data;
uint32_t size = trace->bufferSize;
uint32_t wp = trace->writePtr;
uint32_t rp = trace->readPtr;
if(wp >= rp) {
*value = wp - rp;
}
else {
*value = size - rp + wp;
}
}
else {
uint32_t *value = (uint32_t*) ODF_arg->data;
if(*value == 0) {
/* clear buffer, handle race conditions */
while(trace->readPtr != 0 || trace->writePtr != 0) {
trace->readPtr = 0;
trace->writePtr = 0;
*trace->triggerTime = 0;
}
}
else {
ret = CO_SDO_AB_INVALID_VALUE;
}
}
break;
case 5: /* plot */
if(ODF_arg->reading) {
/* This plot will be transmitted as domain data type. String data
* will be printed directly to SDO buffer. If there is more data
* to print, than is the size of SDO buffer, then this function
* will be called multiple times until internal trace buffer is
* empty. Internal trace buffer is circular buffer. It is accessed
* by this function and by higher priority thread. If this buffer
* is full, there is a danger for race condition. First records
* from trace buffer may be overwritten somewhere between. If this
* is detected, then do{}while() loop tries printing again. */
if(trace->bufferSize == 0 || ODF_arg->dataLength < 100) {
ret = CO_SDO_AB_OUT_OF_MEM;
}
else if(trace->readPtr == trace->writePtr) {
ret = CO_SDO_AB_NO_DATA;
}
else {
uint32_t rp, t, v, len, freeLen;
char *s;
bool_t readPtrOverflowed; /* for handling race conditions */
/* repeat everything, if trace->readPtr was overflowed in CO_trace_process */
do {
readPtrOverflowed = false;
s = (char*) ODF_arg->data;
freeLen = ODF_arg->dataLength;
rp = trace->readPtr;
/* start plot, increment variables, verify overflow */
if(ODF_arg->firstSegment) {
t = trace->timeBuffer[rp];
v = trace->valueBuffer[rp];
rp ++;
if(++trace->readPtr == trace->bufferSize) {
trace->readPtr = 0;
if(rp != trace->bufferSize) {
readPtrOverflowed = true;
continue;
}
rp = 0;
}
if(rp != trace->readPtr) {
readPtrOverflowed = true;
continue;
}
len = trace->dt->printPointStart(s, freeLen, t, v);
s += len;
freeLen -= len;
}
/* print other points */
if(rp != trace->writePtr) {
for(;;) {
t = trace->timeBuffer[rp];
v = trace->valueBuffer[rp];
rp ++;
if(++trace->readPtr == trace->bufferSize) {
trace->readPtr = 0;
if(rp != trace->bufferSize && ODF_arg->firstSegment) {
readPtrOverflowed = true;
break;
}
rp = 0;
}
if(rp != trace->readPtr && ODF_arg->firstSegment) {
readPtrOverflowed = true;
break;
}
/* If internal buffer is empty, end transfer */
if(rp == trace->writePtr) {
/* If there is last time stamp, point will be printed at the end */
if(t != trace->lastTimeStamp) {
len = trace->dt->printPoint(s, freeLen, t, v);
s += len;
freeLen -= len;
}
ODF_arg->lastSegment = true;
break;
}
len = trace->dt->printPoint(s, freeLen, t, v);
s += len;
freeLen -= len;
/* if output buffer is full, next data will be sent later */
if(freeLen < 50) {
ODF_arg->lastSegment = false;
break;
}
}
}
/* print last point */
if(!readPtrOverflowed && ODF_arg->lastSegment) {
v = trace->valuePrev;
t = trace->lastTimeStamp;
len = trace->dt->printPointEnd(s, freeLen, t, v);
s += len;
freeLen -= len;
}
} while(readPtrOverflowed);
ODF_arg->dataLength -= freeLen;
}
}
break;
}
return ret;
}
/******************************************************************************/
void CO_trace_init(
CO_trace_t *trace,
CO_SDO_t *SDO,
uint8_t enabled,
uint32_t *timeBuffer,
int32_t *valueBuffer,
uint32_t bufferSize,
uint32_t *map,
uint8_t *format,
uint8_t *trigger,
int32_t *threshold,
int32_t *value,
int32_t *minValue,
int32_t *maxValue,
uint32_t *triggerTime,
uint16_t idx_OD_traceConfig,
uint16_t idx_OD_trace)
{
trace->SDO = SDO;
trace->enabled = (enabled != 0) ? true : false;
trace->timeBuffer = timeBuffer;
trace->valueBuffer = valueBuffer;
trace->bufferSize = bufferSize;
trace->writePtr = 0;
trace->readPtr = 0;
trace->lastTimeStamp = 0;
trace->map = map;
trace->format = format;
trace->trigger = trigger;
trace->threshold = threshold;
trace->value = value;
trace->minValue = minValue;
trace->maxValue = maxValue;
trace->triggerTime = triggerTime;
*trace->value = 0;
*trace->minValue = 0;
*trace->maxValue = 0;
*trace->triggerTime = 0;
trace->valuePrev = 0;
/* set trace->OD_variable and trace->dt, based on 'map' and 'format' */
findVariable(trace);
if(timeBuffer == NULL || valueBuffer == NULL) {
trace->bufferSize = 0;
}
if( trace->bufferSize == 0 || trace->OD_variable == NULL) {
trace->enabled = false;
}
CO_OD_configure(SDO, idx_OD_traceConfig, CO_ODF_traceConfig, (void*)trace, 0, 0);
CO_OD_configure(SDO, idx_OD_trace, CO_ODF_trace, (void*)trace, 0, 0);
}
/******************************************************************************/
void CO_trace_process(CO_trace_t *trace, uint32_t timestamp) {
if(trace->enabled) {
int32_t val = trace->dt->pGetValue(trace->OD_variable);
if(val != trace->valuePrev) {
/* Verify, if value passed threshold */
if((*trace->trigger & 1) != 0 && trace->valuePrev < *trace->threshold && val >= *trace->threshold) {
*trace->triggerTime = timestamp;
}
if((*trace->trigger & 2) != 0 && trace->valuePrev < *trace->threshold && val >= *trace->threshold) {
*trace->triggerTime = timestamp;
}
/* Write value and verify min/max */
if(trace->value != trace->OD_variable) {
*trace->value = val;
}
trace->valuePrev = val;
if(*trace->minValue > val) {
*trace->minValue = val;
}
if(*trace->maxValue < val) {
*trace->maxValue = val;
}
/* write buffers and update pointers */
trace->timeBuffer[trace->writePtr] = timestamp;
trace->valueBuffer[trace->writePtr] = val;
if(++trace->writePtr == trace->bufferSize) {
trace->writePtr = 0;
}
if(trace->writePtr == trace->readPtr) {
if(++trace->readPtr == trace->bufferSize) {
trace->readPtr = 0;
}
}
}
else {
/* if buffer is empty, make first record */
if(trace->writePtr == trace->readPtr) {
/* write buffers and update pointers */
trace->timeBuffer[trace->writePtr] = timestamp;
trace->valueBuffer[trace->writePtr] = val;
if(++trace->writePtr == trace->bufferSize) {
trace->writePtr = 0;
}
}
}
trace->lastTimeStamp = timestamp;
}
}
#endif /* CO_NO_TRACE */

View File

@ -0,0 +1,174 @@
/**
* CANopen trace interface.
*
* @file CO_trace.h
* @ingroup CO_trace
* @author Janez Paternoster
* @copyright 2016 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CO_TRACE_H
#define CO_TRACE_H
#ifdef __cplusplus
extern "C" {
#endif
#include "CANopen.h"
#if CO_NO_TRACE > 0
/**
* @defgroup CO_trace Trace
* @ingroup CO_CANopen
* @{
*
* CANopen trace for recording variables over time.
*
* In embedded systems there is often a need to monitor some variables over time.
* Results are then displayed on graph, similar as in oscilloscope.
*
* CANopen trace is a configurable object, accessible via CANopen Object
* Dictionary, which records chosen variable over time. It generates a curve,
* which can be read via SDO and can then be displayed in a graph.
*
* CO_trace_process() runs in 1 ms intervals and monitors one variable. If it
* changes, it makes a record with timestamp into circular buffer. When trace is
* accessed by CANopen SDO object, it reads latest points from the the circular
* buffer, prints a SVG curve into string and sends it as a SDO response. If a
* SDO request was received from the same device, then no traffic occupies CAN
* network.
*/
/**
* Start index of traceConfig and Trace objects in Object Dictionary.
*/
#ifndef OD_INDEX_TRACE_CONFIG
#define OD_INDEX_TRACE_CONFIG 0x2301
#define OD_INDEX_TRACE 0x2401
#endif
/**
* structure for reading variables and printing points for specific data type.
*/
typedef struct {
/** Function pointer for getting the value from OD variable. **/
int32_t (*pGetValue) (void *OD_variable);
/** Function pointer for printing the start point to trace.plot */
uint32_t (*printPointStart)(char *s, uint32_t size, uint32_t timeStamp, int32_t value);
/** Function pointer for printing the point to trace.plot */
uint32_t (*printPoint)(char *s, uint32_t size, uint32_t timeStamp, int32_t value);
/** Function pointer for printing the end point to trace.plot */
uint32_t (*printPointEnd)(char *s, uint32_t size, uint32_t timeStamp, int32_t value);
} CO_trace_dataType_t;
/**
* Trace object.
*/
typedef struct {
bool_t enabled; /**< True, if trace is enabled. */
CO_SDO_t *SDO; /**< From CO_trace_init(). */
uint32_t *timeBuffer; /**< From CO_trace_init(). */
int32_t *valueBuffer; /**< From CO_trace_init(). */
uint32_t bufferSize; /**< From CO_trace_init(). */
volatile uint32_t writePtr; /**< Location in buffer, which will be next written. */
volatile uint32_t readPtr; /**< Location in buffer, which will be next read. */
uint32_t lastTimeStamp; /**< Last time stamp. If zero, then last point contains last timestamp. */
void *OD_variable; /**< Pointer to variable, which is monitored */
const CO_trace_dataType_t *dt; /**< Data type specific function pointers. **/
int32_t valuePrev; /**< Previous value of value. */
uint32_t *map; /**< From CO_trace_init(). */
uint8_t *format; /**< From CO_trace_init(). */
int32_t *value; /**< From CO_trace_init(). */
int32_t *minValue; /**< From CO_trace_init(). */
int32_t *maxValue; /**< From CO_trace_init(). */
uint32_t *triggerTime; /**< From CO_trace_init(). */
uint8_t *trigger; /**< From CO_trace_init(). */
int32_t *threshold; /**< From CO_trace_init(). */
} CO_trace_t;
/**
* Initialize trace object.
*
* Function must be called in the communication reset section.
*
* @param trace This object will be initialized.
* @param SDO SDO server object.
* @param enabled Is trace enabled.
* @param timeBuffer Memory block for storing time records.
* @param valueBuffer Memory block for storing value records.
* @param bufferSize Size of the above buffers.
* @param map Map to variable in Object Dictionary, which will be monitored. Same structure as in PDO.
* @param Format Format of the plot. If first bit is 1, above variable is unsigned. For more info see Object Dictionary.
* @param trigger If different than zero, trigger time is recorded, when variable goes through threshold.
* @param threshold Used with trigger.
* @param value Pointer to variable, which will show last value of the variable.
* @param minValue Pointer to variable, which will show minimum value of the variable.
* @param maxValue Pointer to variable, which will show maximum value of the variable.
* @param triggerTime Pointer to variable, which will show last trigger time of the variable.
* @param idx_OD_traceConfig Index in Object Dictionary.
* @param idx_OD_trace Index in Object Dictionary.
*
* @return 0 on success, -1 on error.
*/
void CO_trace_init(
CO_trace_t *trace,
CO_SDO_t *SDO,
uint8_t enabled,
uint32_t *timeBuffer,
int32_t *valueBuffer,
uint32_t bufferSize,
uint32_t *map,
uint8_t *format,
uint8_t *trigger,
int32_t *threshold,
int32_t *value,
int32_t *minValue,
int32_t *maxValue,
uint32_t *triggerTime,
uint16_t idx_OD_traceConfig,
uint16_t idx_OD_trace);
/**
* Process trace object.
*
* Function must be called cyclically in 1ms intervals.
*
* @param trace This object.
* @param timestamp Timestamp (usually in millisecond resolution).
*
* @return 0 on success, -1 on error.
*/
void CO_trace_process(CO_trace_t *trace, uint32_t timestamp);
#endif /* CO_NO_TRACE */
#ifdef __cplusplus
}
#endif /*__cplusplus*/
/** @} */
#endif

View File

@ -0,0 +1,86 @@
/**
* CANopenNode types.
*
* Defines common types for CANopenNode.
*
* @file CO_types.h
* @ingroup CO_CANopen
* @author Olivier Desenfans
* @copyright 2004 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CO_TYPES_H
#define CO_TYPES_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* Return values of some CANopen functions.
*
* A function should return 0 on success and a negative value on error.
*/
typedef enum {
/** Operation completed successfully */
CO_ERROR_NO = 0,
/** Error in function arguments */
CO_ERROR_ILLEGAL_ARGUMENT = -1,
/** Memory allocation failed */
CO_ERROR_OUT_OF_MEMORY = -2,
/** Function timeout */
CO_ERROR_TIMEOUT = -3,
/** Illegal baudrate passed to function CO_CANmodule_init() */
CO_ERROR_ILLEGAL_BAUDRATE = -4,
/** Previous message was not processed yet */
CO_ERROR_RX_OVERFLOW = -5,
/** previous PDO was not processed yet */
CO_ERROR_RX_PDO_OVERFLOW = -6,
/** Wrong receive message length */
CO_ERROR_RX_MSG_LENGTH = -7,
/** Wrong receive PDO length */
CO_ERROR_RX_PDO_LENGTH = -8,
/** Previous message is still waiting, buffer full */
CO_ERROR_TX_OVERFLOW = -9,
/** Synchronous TPDO is outside window */
CO_ERROR_TX_PDO_WINDOW = -10,
/** Transmit buffer was not confugured properly */
CO_ERROR_TX_UNCONFIGURED = -11,
/** Error in function function parameters */
CO_ERROR_PARAMETERS = -12,
/** Stored data are corrupt */
CO_ERROR_DATA_CORRUPT = -13,
/** CRC does not match */
CO_ERROR_CRC = -14,
/** Sending rejected because driver is busy. Try again */
CO_ERROR_TX_BUSY = -15,
/** Command can't be processed in current state */
CO_ERROR_WRONG_NMT_STATE = -16,
/** Syscall failed */
CO_ERROR_SYSCALL = -17,
/** Driver not ready */
CO_ERROR_INVALID_STATE = -18,
} CO_ReturnError_t;
#ifdef __cplusplus
}
#endif /* __cplusplus */
/** @} */
#endif /* CO_TYPES_H */

202
components/canopen/LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,255 @@
CANopenNode
===========
CANopenNode is free and open source CANopen Stack.
CANopen is the internationally standardized (EN 50325-4)
([CiA301](http://can-cia.org/standardization/technical-documents))
CAN-based higher-layer protocol for embedded control system. For more
information on CANopen see http://www.can-cia.org/
CANopenNode is written in ANSI C in object-oriented way. It runs on
different microcontrollers, as standalone application or with RTOS.
Stack includes master functionalities. For Linux implementation with
CANopen master functionalities see
https://github.com/CANopenNode/CANopenSocket.
Variables (communication, device, custom) are ordered in CANopen Object
Dictionary and are accessible from both: C code and from CAN network.
CANopenNode homepage is https://github.com/CANopenNode/CANopenNode
CANopen Features
----------------
- NMT slave to start, stop, reset device. Simple NMT master.
- Heartbeat producer/consumer error control.
- PDO linking and dynamic mapping for fast exchange of process variables.
- SDO expedited, segmented and block transfer for service access to all parameters.
- SDO master.
- Emergency message.
- Sync producer/consumer.
- Time protocol (producer/consumer).
- Non-volatile storage.
- LSS master and slave, LSS fastscan
### RTR
RTR (remote transmission request) is a feature of CAN bus. Usage of RTR
is not recommended for CANopen and it is not implemented in CANopenNode.
### Self start
Object **0x1F80** from Object Dictionary enables the NMT slaves to start
automatically or allows it to start the whole network. It is specified in
DSP302-2 standard. Standard allows two values for slaves for object 0x1F80:
- Object 0x1F80, value = **0x8** - "NMT slave shall enter the NMT state
Operational after the NMT state Initialization autonomously (self starting)"
- Object 0x1F80, value = **0x2** - "NMT slave shall execute the NMT service
start remote node with node-ID set to 0"
Note: When node is stated (in NMT operational state), it is allowed to send or
receive Process Data Objects (PDO). If Error Register (object 0x1001) is set,
then NMT operational state is not allowed.
Usage of the CANopenNode
------------------------
CANopenNode itself doesn't have complete working code for any microcontroller.
It is only the library with the stack It has example, which should compile
on any system with template driver (drvTemplate), which actually doesn't
access CAN hardware. CANopenNode should be used as a git submodule included
in a project with specific hardware and specific application.
Documentation, support and contributions
----------------------------------------
Code is documented in header files. Running [doxygen](http://www.doxygen.nl/)
or `make doc` in project base folder will produce complete html documentation.
Just open CANopenNode/doc/html/index.html in browser.
Report issues on https://github.com/CANopenNode/CANopenNode/issues
Older and still active discussion group is on Sourceforge
http://sourceforge.net/p/canopennode/discussion/387151/
For some implementations of CANopenNode on real hardware see
[Device support](#device-support) section.
[CANopenSocket](https://github.com/CANopenNode/CANopenSocket) is nice
implementation for Linux devices. It includes command line interface for
master access of the CANopen network. There is also some Getting started.
Contributions are welcome. Best way to contribute your code is to fork
a project, modify it and then send a pull request. Some basic formatting
rules should be followed: Linux style with indentation of 4 spaces. There
is also a configuration file for `clang-format` tool.
Flowchart of a typical CANopenNode implementation
-------------------------------------------------
~~~
-----------------------
| Program start |
-----------------------
|
-----------------------
| CANopen init |
-----------------------
|
-----------------------
| Start threads |
-----------------------
| | |
-------------------- | --------------------
| | |
----------------------- ----------------------- -----------------------
| CAN receive thread | | Timer interval thread | | Mainline thread |
| | | | | |
| - Fast response. | | - Realtime thread with| | - Processing of time |
| - Detect CAN ID. | | constant interval, | | consuming tasks |
| - Partially process | | typically 1ms. | | in CANopen objects: |
| messages and copy | | - Network synchronized| | - SDO server, |
| data to target | | - Copy inputs (RPDOs, | | - Emergency, |
| CANopen objects. | | HW) to Object Dict. | | - Network state, |
| | | - May call application| | - Heartbeat. |
| | | for some processing.| | - May cyclically call |
| | | - Copy variables from | | application code. |
| | | Object Dictionary to| | |
| | | outputs (TPDOs, HW).| | |
----------------------- ----------------------- -----------------------
-----------------------
| SDO client (optional) |
| |
| - Can be called by |
| external application|
| - Can read or write |
| any variable from |
| Object Dictionary |
| from any node in the|
| CANopen network. |
-----------------------
-----------------------
| LSS client (optional) |
| |
| - Can be called by |
| external application|
| - Can do LSS requests |
| - Can request node |
| enumeration |
-----------------------
~~~
File structure
--------------
- **CANopen.h/.c** - Initialization and processing of CANopen objects. Most
usual implementation of CANopen device.
- **stack** - Directory with all CANopen objects in separate files.
- **CO_Emergency.h/.c** - CANopen Emergency object.
- **CO_NMT_Heartbeat.h/.c** - CANopen Network slave and Heartbeat producer object.
- **CO_HBconsumer.h/.c** - CANopen Heartbeat consumer object.
- **CO_LSS.h** - CANopen LSS common. This is common to LSS master and slave.
- **CO_LSSmaster.h/.c** - CANopen LSS master functionality.
- **CO_LSSslave.h/.c** - CANopen LSS slave functionality.
- **CO_SYNC.h/.c** - CANopen SYNC producer and consumer object.
- **CO_TIME.h/.c** - CANopen TIME protocol object.
- **CO_SDO.h/.c** - CANopen SDO server object. It serves data from Object dictionary.
- **CO_PDO.h/.c** - CANopen PDO object. It configures, receives and transmits CANopen process data.
- **CO_SDOmaster.h/.c** - CANopen SDO client object (master functionality).
- **CO_trace.h/.c** - Trace object with timestamp for monitoring variables from Object Dictionary (optional).
- **crc16-ccitt.h/.c** - CRC calculation object.
- **drvTemplate** - Directory with microcontroller specific files. In this
case it is template for new implementations. It is also documented, other
directories are not.
- **CO_driver.h/.c** - Microcontroller specific objects for CAN module.
- **eeprom.h/.c** - Functions for storage of Object dictionary, optional.
- **helpers.h/.c** - Some optional files with specific helper functions.
- **socketCAN** - Directory for Linux socketCAN interface.
- **PIC32** - Directory for PIC32 devices from Microchip.
- **PIC24_dsPIC33** - Directory for PIC24 and dsPIC33 devices from Microchip.
- **dsPIC30F** - Directory for dsPIC30F devices from Microchip.
- **eCos** - Directory for all devices supported by eCos RTOS.
- **SAM3X** - Directory for SAM3X ARM Cortex M3 devices with ASF library from Atmel.
- **STM32** - Directory for STM32 ARM devices from ST.
- **LPC177x_8x** - Directory for LPC177x (Cortex M3) devices with FreeRTOS from NXP.
- **MCF5282** - Directory for MCF5282 (ColdFire V2) device from Freescale.
- **codingStyle** - Description of the coding style.
- **Doxyfile** - Configuration file for the documentation generator *doxygen*.
- **Makefile** - Basic makefile.
- **LICENSE** - License.
- **README.md** - This file.
- **example** - Directory with basic example.
- **main.c** - Mainline and other threads - example template.
- **application.h/.c** - Separate file with some functions, which are
called from main.c. May be used for application specific code.
- **CO_OD.h/.c** - CANopen Object dictionary. Automatically generated files.
- **IO.eds** - Standard CANopen EDS file, which may be used from CANopen
configuration tool. Automatically generated file.
- _ **project.xml** - XML file contains all data for CANopen Object dictionary.
It is used by *Object dictionary editor* application, which generates other
files.
- _ **project.html** - *Object dictionary editor* launcher.
### Object dictionary editor
Object Dictionary is one of the most important parts of CANopen. Its
implementation in CANopenNode is quite outdated and there are efforts to
rewrite it. Anyway, currently it is fully operational and works well.
To customize the Object Dictionary it is necessary to use the
external application. There are two:
- [libedssharp](https://github.com/robincornelius/libedssharp) -
recommended, can be used with mono.
- [Object_Dictionary_Editor](http://sourceforge.net/p/canopennode/code_complete/) -
originally part of CANopenNode. It is still operational, but requiers
very old version of Firefox to run.
Device support
--------------
CANopenNode can be implemented on many different devices. It is
necessary to implement interface to specific hardware, so called 'driver'.
Currently driver files are part of CANopenNode, but they will be split from
it in the future.
Most up to date information on device support can be found on
[CANopenNode/wiki](https://github.com/CANopenNode/CANopenNode/wiki).
### Note for contributors
For the driver developers, who wish to share and cooperate, I recommend the following approach:
1. Make own git repo for the Device specific demo project on the Github or somewhere.
2. Add https://github.com/CANopenNode/CANopenNode into your project (or at side of your project).
For example, include it in your project as a git submodule:
`git submodule add https://github.com/CANopenNode/CANopenNode`
3. Add specific driver and other files.
4. **Add a note** about your specific implementation here on
[CANopenNode/wiki](https://github.com/CANopenNode/CANopenNode/wiki) with some
basic description and status. Write a note, even it has an Alpha status.
5. Make a demo folder, which contains project files, etc., necessary to run the demo.
6. Write a good README.md file, where you describe your project, specify demo board, tools used, etc.
History of the project
----------------------
Project was initially hosted on http://sourceforge.net/projects/canopennode/
It started in 2004 with PIC18F microcontrollers from Microchip.
Fresh, cleaned repository of CANopenNode stack started on 25.7.2015.
For older history see http://sourceforge.net/p/canopennode/code_complete/
License
-------
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,99 @@
/*
* Calculation of CRC 16 CCITT polynomial, x^16 + x^12 + x^5 + 1.
*
* @file crc16-ccitt.c
* @ingroup crc16-ccitt
* @author Janez Paternoster
* @copyright 2012 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CO_USE_OWN_CRC16
#include "crc16-ccitt.h"
/*
* CRC table calculated by the following algorithm:
*
* void crc16_ccitt_table_init(void){
* unsigned short i, j;
* for(i=0; i<256; i++){
* unsigned short crc = 0;
* unsigned short c = i << 8;
* for(j=0; j<8; j++){
* if((crc ^ c) & 0x8000) crc = (crc << 1) ^ 0x1021;
* else crc = crc << 1;
* c = c << 1;
* }
* crc16_ccitt_table[i] = crc;
* }
* }
*/
static const unsigned short crc16_ccitt_table[256] = {
0x0000U, 0x1021U, 0x2042U, 0x3063U, 0x4084U, 0x50A5U, 0x60C6U, 0x70E7U,
0x8108U, 0x9129U, 0xA14AU, 0xB16BU, 0xC18CU, 0xD1ADU, 0xE1CEU, 0xF1EFU,
0x1231U, 0x0210U, 0x3273U, 0x2252U, 0x52B5U, 0x4294U, 0x72F7U, 0x62D6U,
0x9339U, 0x8318U, 0xB37BU, 0xA35AU, 0xD3BDU, 0xC39CU, 0xF3FFU, 0xE3DEU,
0x2462U, 0x3443U, 0x0420U, 0x1401U, 0x64E6U, 0x74C7U, 0x44A4U, 0x5485U,
0xA56AU, 0xB54BU, 0x8528U, 0x9509U, 0xE5EEU, 0xF5CFU, 0xC5ACU, 0xD58DU,
0x3653U, 0x2672U, 0x1611U, 0x0630U, 0x76D7U, 0x66F6U, 0x5695U, 0x46B4U,
0xB75BU, 0xA77AU, 0x9719U, 0x8738U, 0xF7DFU, 0xE7FEU, 0xD79DU, 0xC7BCU,
0x48C4U, 0x58E5U, 0x6886U, 0x78A7U, 0x0840U, 0x1861U, 0x2802U, 0x3823U,
0xC9CCU, 0xD9EDU, 0xE98EU, 0xF9AFU, 0x8948U, 0x9969U, 0xA90AU, 0xB92BU,
0x5AF5U, 0x4AD4U, 0x7AB7U, 0x6A96U, 0x1A71U, 0x0A50U, 0x3A33U, 0x2A12U,
0xDBFDU, 0xCBDCU, 0xFBBFU, 0xEB9EU, 0x9B79U, 0x8B58U, 0xBB3BU, 0xAB1AU,
0x6CA6U, 0x7C87U, 0x4CE4U, 0x5CC5U, 0x2C22U, 0x3C03U, 0x0C60U, 0x1C41U,
0xEDAEU, 0xFD8FU, 0xCDECU, 0xDDCDU, 0xAD2AU, 0xBD0BU, 0x8D68U, 0x9D49U,
0x7E97U, 0x6EB6U, 0x5ED5U, 0x4EF4U, 0x3E13U, 0x2E32U, 0x1E51U, 0x0E70U,
0xFF9FU, 0xEFBEU, 0xDFDDU, 0xCFFCU, 0xBF1BU, 0xAF3AU, 0x9F59U, 0x8F78U,
0x9188U, 0x81A9U, 0xB1CAU, 0xA1EBU, 0xD10CU, 0xC12DU, 0xF14EU, 0xE16FU,
0x1080U, 0x00A1U, 0x30C2U, 0x20E3U, 0x5004U, 0x4025U, 0x7046U, 0x6067U,
0x83B9U, 0x9398U, 0xA3FBU, 0xB3DAU, 0xC33DU, 0xD31CU, 0xE37FU, 0xF35EU,
0x02B1U, 0x1290U, 0x22F3U, 0x32D2U, 0x4235U, 0x5214U, 0x6277U, 0x7256U,
0xB5EAU, 0xA5CBU, 0x95A8U, 0x8589U, 0xF56EU, 0xE54FU, 0xD52CU, 0xC50DU,
0x34E2U, 0x24C3U, 0x14A0U, 0x0481U, 0x7466U, 0x6447U, 0x5424U, 0x4405U,
0xA7DBU, 0xB7FAU, 0x8799U, 0x97B8U, 0xE75FU, 0xF77EU, 0xC71DU, 0xD73CU,
0x26D3U, 0x36F2U, 0x0691U, 0x16B0U, 0x6657U, 0x7676U, 0x4615U, 0x5634U,
0xD94CU, 0xC96DU, 0xF90EU, 0xE92FU, 0x99C8U, 0x89E9U, 0xB98AU, 0xA9ABU,
0x5844U, 0x4865U, 0x7806U, 0x6827U, 0x18C0U, 0x08E1U, 0x3882U, 0x28A3U,
0xCB7DU, 0xDB5CU, 0xEB3FU, 0xFB1EU, 0x8BF9U, 0x9BD8U, 0xABBBU, 0xBB9AU,
0x4A75U, 0x5A54U, 0x6A37U, 0x7A16U, 0x0AF1U, 0x1AD0U, 0x2AB3U, 0x3A92U,
0xFD2EU, 0xED0FU, 0xDD6CU, 0xCD4DU, 0xBDAAU, 0xAD8BU, 0x9DE8U, 0x8DC9U,
0x7C26U, 0x6C07U, 0x5C64U, 0x4C45U, 0x3CA2U, 0x2C83U, 0x1CE0U, 0x0CC1U,
0xEF1FU, 0xFF3EU, 0xCF5DU, 0xDF7CU, 0xAF9BU, 0xBFBAU, 0x8FD9U, 0x9FF8U,
0x6E17U, 0x7E36U, 0x4E55U, 0x5E74U, 0x2E93U, 0x3EB2U, 0x0ED1U, 0x1EF0U
};
/******************************************************************************/
unsigned short crc16_ccitt(
const unsigned char block[],
unsigned int blockLength,
unsigned short crc)
{
unsigned int i;
for(i=0U; i<blockLength; i++){
unsigned short tmp = (crc >> 8) ^ (unsigned short) block[i];
crc = ((unsigned short)(crc << 8U)) ^ crc16_ccitt_table[tmp];
}
return crc;
}
#endif /* CO_USE_OWN_CRC16 */

View File

@ -0,0 +1,71 @@
/**
* Calculation of CRC 16 CCITT polynomial.
*
* @file crc16-ccitt.h
* @ingroup CO_crc16_ccitt
* @author Lammert Bies
* @author Janez Paternoster
* @copyright 2012 - 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CRC16_CCITT_H
#define CRC16_CCITT_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup CO_crc16_ccitt CRC 16 CCITT
* @ingroup CO_CANopen
* @{
*
* Calculation of CRC 16 CCITT polynomial.
*
* Equation:
*
* `x^16 + x^12 + x^5 + 1`
*/
/**
* Calculate CRC sum on block of data.
*
* @param block Pointer to block of data.
* @param blockLength Length of data in bytes;
* @param crc Initial value (zero for xmodem). If block is split into
* multiple segments, previous CRC is used as initial.
*
* @return Calculated CRC.
*/
#ifdef CO_USE_OWN_CRC16
extern
#endif
unsigned short crc16_ccitt(
const unsigned char block[],
unsigned int blockLength,
unsigned short crc);
#ifdef __cplusplus
}
#endif /*__cplusplus*/
/** @} */
#endif

View File

@ -0,0 +1,23 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[Makefile]
indent_style = tab
indent_size = unset
# ignore external repositories and test inputs
[tests/{unity,json-patch-tests,inputs}/*]
indent_style = unset
indent_size = unset
end_of_line = unset
charset = unset
trim_trailing_whitespace = unset
insert_final_newline = unset

11
components/cjson/.gitattributes vendored Normal file
View File

@ -0,0 +1,11 @@
* text=auto
/tests/inputs/* text eol=lf
.gitattributes export-ignore
.gitignore export-ignore
.github export-ignore
.editorconfig export-ignore
.travis.yml export-ignore
# Linguist incorrectly identified the headers as C++, manually override this.
*.h linguist-language=C

20
components/cjson/.gitignore vendored Normal file
View File

@ -0,0 +1,20 @@
.svn
test
*.o
*.a
*.so
*.swp
*.patch
tags
*.dylib*
build/
cJSON_test
cJSON_test_utils
libcjson.so.*
libcjson_utils.so.*
*.orig
.vscode
.idea
cmake-build-debug
*.lst
*.lss

View File

@ -0,0 +1,28 @@
dist: trusty
sudo: false
language: c
env:
matrix:
- VALGRIND=On SANITIZERS=Off
- VALGRIND=Off SANITIZERS=Off
- VALGRIND=Off SANITIZERS=On
compiler:
- gcc
- clang
addons:
apt:
packages:
- valgrind
- libasan0
- lib32asan0
# currently not supported on travis:
# - libasan1
# - libasan2
# - libubsan0
- llvm
script:
- mkdir build
- cd build
- cmake .. -DENABLE_CJSON_UTILS=On -DENABLE_VALGRIND="${VALGRIND}" -DENABLE_SAFE_STACK="${VALGRIND}" -DENABLE_SANITIZERS="${SANITIZERS}"
- make
- make test CTEST_OUTPUT_ON_FAILURE=On

View File

@ -0,0 +1,428 @@
1.7.15 (Aug 25, 2021)
======
Fixes:
------
* Fix potential core dumped for strrchr, see [#546](https://github.com/DaveGamble/cJSON/pull/546)
* Fix null pointer crash in cJSON_CreateXxArray, see [#538](https://github.com/DaveGamble/cJSON/pull/538)
* Fix several null pointer problems on allocation failure, see [#526](https://github.com/DaveGamble/cJSON/pull/526)
* Fix a possible dereference of null pointer, see [#519](https://github.com/DaveGamble/cJSON/pull/519)
* Fix windows build failure about defining nan, see [#518](https://github.com/DaveGamble/cJSON/pull/518)
1.7.14 (Sep 3, 2020)
======
Fixes:
------
* optimize the way to find tail node, see [#503](https://github.com/DaveGamble/cJSON/pull/503)
* Fix WError error on macosx because NAN is a float. Thanks @sappo, see [#484](https://github.com/DaveGamble/cJSON/pull/484)
* Fix some bugs in detach and replace. Thanks @miaoerduo, see [#456](https://github.com/DaveGamble/cJSON/pull/456)
1.7.13 (Apr 2, 2020)
======
Features:
---------
* add new API of cJSON_ParseWithLength without breaking changes. Thanks @caglarivriz, see [#358](https://github.com/DaveGamble/cJSON/pull/358)
* add new API of cJSON_GetNumberValue. Thanks @Intuition, see[#385](https://github.com/DaveGamble/cJSON/pull/385)
* add uninstall target function for CMake. See [#402](https://github.com/DaveGamble/cJSON/pull/402)
* Improve performance of adding item to array. Thanks @xiaomianhehe, see [#430](https://github.com/DaveGamble/cJSON/pull/430), [#448](https://github.com/DaveGamble/cJSON/pull/448)
* add new API of cJSON_SetValuestring, for changing the valuestring safely. See [#451](https://github.com/DaveGamble/cJSON/pull/451)
* add return value for cJSON_AddItemTo... and cJSON_ReplaceItem... (check if the operation successful). See [#453](https://github.com/DaveGamble/cJSON/pull/453)
Fixes:
------
* Fix clang -Wfloat-equal warning. Thanks @paulmalovanyi, see [#368](https://github.com/DaveGamble/cJSON/pull/368)
* Fix make failed in mac os. See [#405](https://github.com/DaveGamble/cJSON/pull/405)
* Fix memory leak in cJSONUtils_FindPointerFromObjectTo. Thanks @andywolk for reporting, see [#414](https://github.com/DaveGamble/cJSON/issues/414)
* Fix bug in encode_string_as_pointer. Thanks @AIChangJiang for reporting, see [#439](https://github.com/DaveGamble/cJSON/issues/439)
1.7.12 (May 17, 2019)
======
Fixes:
------
* Fix infinite loop in `cJSON_Minify` (potential Denial of Service). Thanks @Alanscut for reporting, see [#354](https://github.com/DaveGamble/cJSON/issues/354)
* Fix link error for Visual Studio. Thanks @tan-wei, see [#352](https://github.com/DaveGamble/cJSON/pull/352).
* Undefine `true` and `false` for `cJSON_Utils` before redefining them. Thanks @raiden00pl, see [#347](https://github.com/DaveGamble/cJSON/pull/347).
1.7.11 (Apr 15, 2019)
======
Fixes:
------
* Fix a bug where cJSON_Minify could overflow it's buffer, both reading and writing. This is a security issue, see [#338](https://github.com/DaveGamble/cJSON/issues/338). Big thanks @bigric3 for reporting.
* Unset `true` and `false` macros before setting them if they exist. See [#339](https://github.com/DaveGamble/cJSON/issues/339), thanks @raiden00pl for reporting
1.7.10 (Dec 21, 2018)
======
Fixes:
------
* Fix package config file for `libcjson`. Thanks @shiluotang for reporting [#321](https://github.com/DaveGamble/cJSON/issues/321)
* Correctly split lists in `cJSON_Utils`'s merge sort. Thanks @andysCaplin for the fix [#322](https://github.com/DaveGamble/cJSON/issues/322)
1.7.9 (Dec 16, 2018)
=====
Fixes:
------
* Fix a bug where `cJSON_GetObjectItemCaseSensitive` would pass a nullpointer to `strcmp` when called on an array, see [#315](https://github.com/DaveGamble/cJSON/issues/315). Thanks @yuweol for reporting.
* Fix error in `cJSON_Utils` where the case sensitivity was not respected, see [#317](https://github.com/DaveGamble/cJSON/pull/317). Thanks @yuta-oxo for fixing.
* Fix some warnings detected by the Visual Studio Static Analyzer, see [#307](https://github.com/DaveGamble/cJSON/pull/307). Thanks @bnason-nf
1.7.8 (Sep 22, 2018)
======
Fixes:
------
* cJSON now works with the `__stdcall` calling convention on Windows, see [#295](https://github.com/DaveGamble/cJSON/pull/295), thanks @zhindes for contributing
1.7.7 (May 22, 2018)
=====
Fixes:
------
* Fix a memory leak when realloc fails, see [#267](https://github.com/DaveGamble/cJSON/issues/267), thanks @AlfieDeng for reporting
* Fix a typo in the header file, see [#266](https://github.com/DaveGamble/cJSON/pull/266), thanks @zhaozhixu
1.7.6 (Apr 13, 2018)
=====
Fixes:
------
* Add `SONAME` to the ELF files built by the Makefile, see [#252](https://github.com/DaveGamble/cJSON/issues/252), thanks @YanhaoMo for reporting
* Add include guards and `extern "C"` to `cJSON_Utils.h`, see [#256](https://github.com/DaveGamble/cJSON/issues/256), thanks @daschfg for reporting
Other changes:
* Mark the Makefile as deprecated in the README.
1.7.5 (Mar 23, 2018)
=====
Fixes:
------
* Fix a bug in the JSON Patch implementation of `cJSON Utils`, see [#251](https://github.com/DaveGamble/cJSON/pull/251), thanks @bobkocisko.
1.7.4 (Mar 3, 2018)
=====
Fixes:
------
* Fix potential use after free if the `string` parameter to `cJSON_AddItemToObject` is an alias of the `string` property of the object that is added,see [#248](https://github.com/DaveGamble/cJSON/issues/248). Thanks @hhallen for reporting.
1.7.3 (Feb 8, 2018)
=====
Fixes:
------
* Fix potential double free, thanks @projectgus for reporting [#241](https://github.com/DaveGamble/cJSON/issues/241)
1.7.2 (Feb 6, 2018)
=====
Fixes:
------
* Fix the use of GNUInstallDirs variables and the pkgconfig file. Thanks @zeerd for reporting [#240](https://github.com/DaveGamble/cJSON/pull/240)
1.7.1 (Jan 10, 2018)
=====
Fixes:
------
* Fixed an Off-By-One error that could lead to an out of bounds write. Thanks @liuyunbin for reporting [#230](https://github.com/DaveGamble/cJSON/issues/230)
* Fixed two errors with buffered printing. Thanks @liuyunbin for reporting [#230](https://github.com/DaveGamble/cJSON/issues/230)
1.7.0 (Dec 31, 2017)
=====
Features:
---------
* Large rewrite of the documentation, see [#215](https://github.com/DaveGamble/cJSON/pull/215)
* Added the `cJSON_GetStringValue` function
* Added the `cJSON_CreateStringReference` function
* Added the `cJSON_CreateArrayReference` function
* Added the `cJSON_CreateObjectReference` function
* The `cJSON_Add...ToObject` macros are now functions that return a pointer to the added item, see [#226](https://github.com/DaveGamble/cJSON/pull/226)
Fixes:
------
* Fix a problem with `GNUInstallDirs` in the CMakeLists.txt, thanks @yangfl, see [#210](https://github.com/DaveGamble/cJSON/pull/210)
* Fix linking the tests when building as static library, see [#213](https://github.com/DaveGamble/cJSON/issues/213)
* New overrides for the CMake option `BUILD_SHARED_LIBS`, see [#207](https://github.com/DaveGamble/cJSON/issues/207)
Other Changes:
--------------
* Readme: Explain how to include cJSON, see [#211](https://github.com/DaveGamble/cJSON/pull/211)
* Removed some trailing spaces in the code, thanks @yangfl, see [#212](https://github.com/DaveGamble/cJSON/pull/212)
* Updated [Unity](https://github.com/ThrowTheSwitch/Unity) and [json-patch-tests](https://github.com/json-patch/json-patch-tests)
1.6.0 (Oct 9, 2017)
=====
Features:
---------
* You can now build cJSON as both shared and static library at once with CMake using `-DBUILD_SHARED_AND_STATIC_LIBS=On`, see [#178](https://github.com/DaveGamble/cJSON/issues/178)
* UTF-8 byte order marks are now ignored, see [#184](https://github.com/DaveGamble/cJSON/issues/184)
* Locales can now be disabled with the option `-DENABLE_LOCALES=Off`, see [#202](https://github.com/DaveGamble/cJSON/issues/202), thanks @Casperinous
* Better support for MSVC and Visual Studio
Other Changes:
--------------
* Add the new warnings `-Wswitch-enum`, `-Wused-but-makred-unused`, `-Wmissing-variable-declarations`, `-Wunused-macro`
* More number printing tests.
* Continuous integration testing with AppVeyor (semi automatic at this point), thanks @simon-p-r
1.5.9 (Sep 8, 2017)
=====
Fixes:
------
* Set the global error pointer even if `return_parse_end` is passed to `cJSON_ParseWithOpts`, see [#200](https://github.com/DaveGamble/cJSON/pull/200), thanks @rmallins
1.5.8 (Aug 21, 2017)
=====
Fixes:
------
* Fix `make test` in the Makefile, thanks @YanhaoMo for reporting this [#195](https://github.com/DaveGamble/cJSON/issues/195)
1.5.7 (Jul 13, 2017)
=====
Fixes:
------
* Fix a bug where realloc failing would return a pointer to an invalid memory address. This is a security issue as it could potentially be used by an attacker to write to arbitrary memory addresses, see [#189](https://github.com/DaveGamble/cJSON/issues/189), fixed in [954d61e](https://github.com/DaveGamble/cJSON/commit/954d61e5e7cb9dc6c480fc28ac1cdceca07dd5bd), big thanks @timothyjohncarney for reporting this issue
* Fix a spelling mistake in the AFL fuzzer dictionary, see [#185](https://github.com/DaveGamble/cJSON/pull/185), thanks @jwilk
1.5.6 (Jun 28, 2017)
=====
Fixes:
------
* Make cJSON a lot more tolerant about passing NULL pointers to its functions, it should now fail safely instead of dereferencing the pointer, see [#183](https://github.com/DaveGamble/cJSON/pull/183). Thanks @msichal for reporting [#182](https://github.com/DaveGamble/cJSON/issues/182)
1.5.5 (Jun 15, 2017)
=====
Fixes:
------
* Fix pointers to nested arrays in cJSON_Utils, see [9abe](https://github.com/DaveGamble/cJSON/commit/9abe75e072050f34732a7169740989a082b65134)
* Fix an error with case sensitivity handling in cJSON_Utils, see [b9cc911](https://github.com/DaveGamble/cJSON/commit/b9cc911831b0b3e1bb72f142389428e59f882b38)
* Fix cJSON_Compare for arrays that are prefixes of the other and objects that are a subset of the other, see [03ba72f](https://github.com/DaveGamble/cJSON/commit/03ba72faec115160d1f3aea5582d9b6af5d3e473) and [#180](https://github.com/DaveGamble/cJSON/issues/180), thanks @zhengqb for reporting
1.5.4 (Jun 5, 2017)
======
Fixes:
------
* Fix build with GCC 7.1.1 and optimization level `-O2`, see [bfbd8fe](https://github.com/DaveGamble/cJSON/commit/bfbd8fe0d85f1dd21e508748fc10fc4c27cc51be)
Other Changes:
--------------
* Update [Unity](https://github.com/ThrowTheSwitch/Unity) to 3b69beaa58efc41bbbef70a32a46893cae02719d
1.5.3 (May 23, 2017)
=====
Fixes:
------
* Fix `cJSON_ReplaceItemInObject` not keeping the name of an item, see [#174](https://github.com/DaveGamble/cJSON/issues/174)
1.5.2 (May 10, 2017)
=====
Fixes:
------
* Fix a reading buffer overflow in `parse_string`, see [a167d9e](https://github.com/DaveGamble/cJSON/commit/a167d9e381e5c84bc03de4e261757b031c0c690d)
* Fix compiling with -Wcomma, see [186cce3](https://github.com/DaveGamble/cJSON/commit/186cce3ece6ce6dfcb58ac8b2a63f7846c3493ad)
* Remove leftover attribute from tests, see [b537ca7](https://github.com/DaveGamble/cJSON/commit/b537ca70a35680db66f1f5b8b437f7114daa699a)
1.5.1 (May 6, 2017)
=====
Fixes:
------
* Add gcc version guard to the Makefile, see [#164](https://github.com/DaveGamble/cJSON/pull/164), thanks @juvasquezg
* Fix incorrect free in `cJSON_Utils` if custom memory allocator is used, see [#166](https://github.com/DaveGamble/cJSON/pull/166), thanks @prefetchnta
1.5.0 (May 2, 2017)
=====
Features:
* cJSON finally prints numbers without losing precision, see [#153](https://github.com/DaveGamble/cJSON/pull/153), thanks @DeboraG
* `cJSON_Compare` recursively checks if two cJSON items contain the same values, see [#148](https://github.com/DaveGamble/cJSON/pull/148)
* Provide case sensitive versions of every function where it matters, see [#158](https://github.com/DaveGamble/cJSON/pull/158) and [#159](https://github.com/DaveGamble/cJSON/pull/159)
* Added `cJSON_ReplaceItemViaPointer` and `cJSON_DetachItemViaPointer`
* Added `cJSON_free` and `cJSON_malloc` that expose the internal configured memory allocators. see [02a05ee](https://github.com/DaveGamble/cJSON/commit/02a05eea4e6ba41811f130b322660bea8918e1a0)
Enhancements:
-------------
* Parse into a buffer, this will allow parsing `\u0000` in the future (not quite yet though)
* General simplifications and readability improvements
* More unit tests
* Update [unity](https://github.com/ThrowTheSwitch/Unity) testing library to 2.4.1
* Add the [json-patch-tests](https://github.com/json-patch/json-patch-tests) test suite to test cJSON_Utils.
* Move all tests from `test_utils.c` to unit tests with unity.
Fixes:
------
* Fix some warnings with the Microsoft compiler, see [#139](https://github.com/DaveGamble/cJSON/pull/139), thanks @PawelWMS
* Fix several bugs in cJSON_Utils, mostly found with [json-patch-tests](https://github.com/json-patch/json-patch-tests)
* Prevent a stack overflow by specifying a maximum nesting depth `CJSON_NESTING_LIMIT`
Other Changes:
--------------
* Move generated files in the `library_config` subdirectory.
1.4.7 (Apr 19, 2017)
=====
Fixes:
------
* Fix `cJSONUtils_ApplyPatches`, it was completely broken and apparently nobody noticed (or at least reported it), see [075a06f](https://github.com/DaveGamble/cJSON/commit/075a06f40bdc4f836c7dd7cad690d253a57cfc50)
* Fix inconsistent prototype for `cJSON_GetObjectItemCaseSensitive`, see [51d3df6](https://github.com/DaveGamble/cJSON/commit/51d3df6c9f7b56b860c8fb24abe7bab255cd4fa9), thanks @PawelWMS
1.4.6 (Apr 9, 2017)
=====
Fixes:
------
* Several corrections in the README
* Making clear that `valueint` should not be written to
* Fix overflow detection in `ensure`, see [2683d4d](https://github.com/DaveGamble/cJSON/commit/2683d4d9873df87c4bdccc523903ddd78d1ad250)
* Fix a potential null pointer dereference in cJSON_Utils, see [795c3ac](https://github.com/DaveGamble/cJSON/commit/795c3acabed25c9672006b2c0f40be8845064827)
* Replace incorrect `sizeof('\0')` with `sizeof("")`, see [84237ff](https://github.com/DaveGamble/cJSON/commit/84237ff48e69825c94261c624eb0376d0c328139)
* Add caveats section to the README, see [50b3c30](https://github.com/DaveGamble/cJSON/commit/50b3c30dfa89830f8f477ce33713500740ac3b79)
* Make cJSON locale independent, see [#146](https://github.com/DaveGamble/cJSON/pull/146), Thanks @peterh for reporting
* Fix compiling without CMake with MSVC, see [#147](https://github.com/DaveGamble/cJSON/pull/147), Thanks @dertuxmalwieder for reporting
1.4.5 (Mar 28, 2017)
=====
Fixes:
------
* Fix bug in `cJSON_SetNumberHelper`, thanks @mmkeeper, see [#138](https://github.com/DaveGamble/cJSON/issues/138) and [ef34500](https://github.com/DaveGamble/cJSON/commit/ef34500693e8c4a2849d41a4bd66fd19c9ec46c2)
* Workaround for internal compiler error in GCC 5.4.0 and 6.3.1 on x86 (2f65e80a3471d053fdc3f8aed23d01dd1782a5cb [GCC bugreport](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80097))
1.4.4 (Mar 24, 2017)
=====
Fixes:
------
* Fix a theoretical integer overflow, (not sure if it is possible on actual hardware), see [e58f7ec](https://github.com/DaveGamble/cJSON/commit/e58f7ec027d00b7cdcbf63e518c1b5268b29b3da)
* Fix an off by one error, see [cc84a44](https://github.com/DaveGamble/cJSON/commit/cc84a446be20cc283bafdc4d94c050ba1111ac02), thanks @gatzka
* Double check the offset of the print buffer in `ensure`, see [1934059](https://github.com/DaveGamble/cJSON/commit/1934059554b9a0971e00f79e96900f422cfdd114)
Improvements:
* Add a note in the header about required buffer size when using `cJSON_PrintPreallocated`, see [4bfb8800](https://github.com/DaveGamble/cJSON/commit/4bfb88009342fb568295a7f6dc4b7fee74fbf022)
1.4.3 (Mar 19, 2017)
=====
Fixes:
------
* Fix compilation of the tests on 32 bit PowerPC and potentially other systems, see [4ec6e76](https://github.com/DaveGamble/cJSON/commit/4ec6e76ea2eec16f54b58e8c95b4c734e59481e4)
* Fix compilation with old GCC compilers (4.3+ were tested), see [227d33](https://github.com/DaveGamble/cJSON/commit/227d3398d6b967879761ebe02c1b63dbd6ea6e0d), [466eb8e](https://github.com/DaveGamble/cJSON/commit/466eb8e3f8a65080f2b3ca4a79ab7b72bd539dba), see also [#126](https://github.com/DaveGamble/cJSON/issues/126)
1.4.2 (Mar 16, 2017)
=====
Fixes:
------
* Fix minimum required cmake version, see [30e1e7a](https://github.com/DaveGamble/cJSON/commit/30e1e7af7c63db9b55f5a3cda977a6c032f0b132)
* Fix detection of supported compiler flags, see [76e5296](https://github.com/DaveGamble/cJSON/commit/76e5296d0d05ceb3018a9901639e0e171b44a557)
* Run `cJSON_test` and `cJSON_test_utils` along with unity tests, see [c597601](https://github.com/DaveGamble/cJSON/commit/c597601cf151a757dcf800548f18034d4ddfe2cb)
1.4.1 (Mar 16, 2017)
=====
Fixes:
------
* Make `print_number` abort with a failure in out of memory situations, see [cf1842](https://github.com/DaveGamble/cJSON/commit/cf1842dc6f64c49451a022308b4415e4d468be0a)
1.4.0 (Mar 4, 2017)
=====
Features
--------
* Functions to check the type of an item, see [#120](https://github.com/DaveGamble/cJSON/pull/120)
* Use dllexport on windows and fvisibility on Unix systems for public functions, see [#116](https://github.com/DaveGamble/cJSON/pull/116), thanks @mjerris
* Remove trailing zeroes from printed numbers, see [#123](https://github.com/DaveGamble/cJSON/pull/123)
* Expose the internal boolean type `cJSON_bool` in the header, see [2d3520e](https://github.com/DaveGamble/cJSON/commit/2d3520e0b9d0eb870e8886e8a21c571eeddbb310)
Fixes
* Fix handling of NULL pointers in `cJSON_ArrayForEach`, see [b47d0e3](https://github.com/DaveGamble/cJSON/commit/b47d0e34caaef298edfb7bd09a72cfff21d231ff)
* Make it compile with GCC 7 (fix -Wimplicit-fallthrough warning), see [9d07917](https://github.com/DaveGamble/cJSON/commit/9d07917feb1b613544a7513d19233d4c851ad7ad)
Other Improvements
* internally use realloc if available ([#110](https://github.com/DaveGamble/cJSON/pull/110))
* builtin support for fuzzing with [afl](http://lcamtuf.coredump.cx/afl/) ([#111](https://github.com/DaveGamble/cJSON/pull/111))
* unit tests for the print functions ([#112](https://github.com/DaveGamble/cJSON/pull/112))
* Always use buffered printing ([#113](https://github.com/DaveGamble/cJSON/pull/113))
* simplify the print functions ([#114](https://github.com/DaveGamble/cJSON/pull/114))
* Add the compiler flags `-Wdouble-conversion`, `-Wparentheses` and `-Wcomma` ([#122](https://github.com/DaveGamble/cJSON/pull/122))
1.3.2 (Mar 1, 2017)
=====
Fixes:
------
* Don't build the unity library if testing is disabled, see [#121](https://github.com/DaveGamble/cJSON/pull/121). Thanks @ffontaine
1.3.1 (Feb 27, 2017)
=====
Fixes:
------
* Bugfix release that fixes an out of bounds read, see [#118](https://github.com/DaveGamble/cJSON/pull/118). This shouldn't have any security implications.
1.3.0 (Feb 17, 2017)
=====
This release includes a lot of rework in the parser and includes the Cunity unit testing framework, as well as some fixes. I increased the minor version number because there were quite a lot of internal changes.
Features:
* New type for cJSON structs: `cJSON_Invalid`, see [#108](https://github.com/DaveGamble/cJSON/pull/108)
Fixes:
------
* runtime checks for a lot of potential integer overflows
* fix incorrect return in cJSON_PrintBuffered [cf9d57d](https://github.com/DaveGamble/cJSON/commit/cf9d57d56cac21fc59465b8d26cf29bf6d2a87b3)
* fix several potential issues found by [Coverity](https://scan.coverity.com/projects/cjson)
* fix potentially undefined behavior when assigning big numbers to `valueint` ([41e2837](https://github.com/DaveGamble/cJSON/commit/41e2837df1b1091643aff073f2313f6ff3cc10f4))
* Numbers exceeding `INT_MAX` or lower than `INT_MIN` will be explicitly assigned to `valueint` as `INT_MAX` and `INT_MIN` respectively (saturation on overflow).
* fix the `cJSON_SetNumberValue` macro ([87f7727](https://github.com/DaveGamble/cJSON/commit/87f77274de6b3af00fb9b9a7f3b900ef382296c2)), this slightly changes the behavior, see commit message
Introduce unit tests
--------------------
* Started writing unit tests with the [Cunity](https://github.com/ThrowTheSwitch/Unity) testing framework. Currently this covers the parser functions.
Also:
* Support for running the tests with [Valgrind](http://valgrind.org)
* Support for compiling the tests with [AddressSanitizer](https://github.com/google/sanitizers) and [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html).
* `travis.yml` file for running unit tests on travis. (not enabled for the repository yet though [#102](https://github.com/DaveGamble/cJSON/issues/102)
Simplifications
---------------
After having unit tests for the parser function in place, I started refactoring the parser functions (as well as others) and making them easier to read and maintain.
* Use `strtod` from the standard library for parsing numbers ([0747669](https://github.com/DaveGamble/cJSON/commit/074766997246481dfc72bfa78f07898a2716473f))
* Use goto-fail in several parser functions ([#100](https://github.com/DaveGamble/cJSON/pull/100))
* Rewrite/restructure all of the parsing functions to be easier to understand and have less code paths doing the same as another. ([#109](https://github.com/DaveGamble/cJSON/pull/109))
* Simplify the buffer allocation strategy to always doubling the needed amount ([9f6fa94](https://github.com/DaveGamble/cJSON/commit/9f6fa94c91a87b71e4c6868dbf2ce431a48517b0))
* Combined `cJSON_AddItemToObject` and `cJSON_AddItemToObjectCS` to one function ([cf862d](https://github.com/DaveGamble/cJSON/commit/cf862d0fed7f9407e4b046d78d3d8050d2080d12))
Other changes
-------------
* Prevent the usage of incompatible C and header versions via preprocessor directive ([123bb1](https://github.com/DaveGamble/cJSON/commit/123bb1af7bfae41d805337fef4b41045ef6c7d25))
* Let CMake automatically detect compiler flags
* Add new compiler flags (`-Wundef`, `-Wswitch-default`, `-Wconversion`, `-fstack-protector-strong`) ([#98](https://github.com/DaveGamble/cJSON/pull/98))
* Change internal sizes from `int` to `size_t` ([ecd5678](https://github.com/DaveGamble/cJSON/commit/ecd5678527a6bc422da694e5be9e9979878fe6a0))
* Change internal strings from `char*` to `unsigned char*` ([28b9ba4](https://github.com/DaveGamble/cJSON/commit/28b9ba4334e0f7309e867e874a31f395c0ac2474))
* Add `const` in more places
1.2.1 (Jan 31, 2017)
=====
Fixes:
------
* Fixes a potential null pointer dereference in cJSON_Utils, discovered using clang's static analyzer by @bnason-nf, see [#96](https://github.com/DaveGamble/cJSON/issues/96)
1.2.0 (Jan 9, 2017)
=====
Features:
---------
* Add a new type of cJSON item for raw JSON and support printing it. Thanks @loigu, see [#65](https://github.com/DaveGamble/cJSON/pull/65), [#90](https://github.com/DaveGamble/cJSON/pull/90)
Fixes:
------
* Compiler warning if const is casted away, Thanks @gatzka, see [#83](https://github.com/DaveGamble/cJSON/pull/83)
* Fix compile error with strict-overflow on PowerPC, see [#85](https://github.com/DaveGamble/cJSON/issues/85)
* Fix typo in the README, thanks @MicroJoe, see [#88](https://github.com/DaveGamble/cJSON/pull/88)
* Add compile flag for compatibility with C++ compilers
1.1.0 (Dec 6, 2016)
=====
* Add a function `cJSON_PrintPreallocated` to print to a preallocated buffer, thanks @ChisholmKyle, see [#72](https://github.com/DaveGamble/cJSON/pull/72)
* More compiler warnings when using Clang or GCC, thanks @gatzka, see [#75](https://github.com/DaveGamble/cJSON/pull/75), [#78](https://github.com/DaveGamble/cJSON/pull/78)
* fixed a memory leak in `cJSON_Duplicate`, thanks @alperakcan, see [#81](https://github.com/DaveGamble/cJSON/pull/81)
* fix the `ENABLE_CUSTOM_COMPILER_FLAGS` cmake option
1.0.2 (Nov 25, 2016)
=====
* Rename internal boolean type, see [#71](https://github.com/DaveGamble/cJSON/issues/71).
1.0.1 (Nov 20, 2016)
=====
Small bugfix release.
* Fixes a bug with the use of the cJSON structs type in cJSON_Utils, see [d47339e](https://github.com/DaveGamble/cJSON/commit/d47339e2740360e6e0994527d5e4752007480f3a)
* improve code readability
* initialize all variables
1.0.0 (Nov 17, 2016)
=====
This is the first official versioned release of cJSON. It provides an API version for the shared library and improved Makefile and CMake build files.

View File

@ -0,0 +1,283 @@
set(CMAKE_LEGACY_CYGWIN_WIN32 0)
cmake_minimum_required(VERSION 3.0)
project(cJSON
VERSION 1.7.15
LANGUAGES C)
cmake_policy(SET CMP0054 NEW) # set CMP0054 policy
include(GNUInstallDirs)
set(CJSON_VERSION_SO 1)
set(CJSON_UTILS_VERSION_SO 1)
set(custom_compiler_flags)
include(CheckCCompilerFlag)
option(ENABLE_CUSTOM_COMPILER_FLAGS "Enables custom compiler flags" ON)
if (ENABLE_CUSTOM_COMPILER_FLAGS)
if (("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") OR ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU"))
list(APPEND custom_compiler_flags
-std=c89
-pedantic
-Wall
-Wextra
-Werror
-Wstrict-prototypes
-Wwrite-strings
-Wshadow
-Winit-self
-Wcast-align
-Wformat=2
-Wmissing-prototypes
-Wstrict-overflow=2
-Wcast-qual
-Wundef
-Wswitch-default
-Wconversion
-Wc++-compat
-fstack-protector-strong
-Wcomma
-Wdouble-promotion
-Wparentheses
-Wformat-overflow
-Wunused-macros
-Wmissing-variable-declarations
-Wused-but-marked-unused
-Wswitch-enum
)
elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
# Disable warning c4001 - nonstandard extension 'single line comment' was used
# Define _CRT_SECURE_NO_WARNINGS to disable deprecation warnings for "insecure" C library functions
list(APPEND custom_compiler_flags
/GS
/Za
/sdl
/W4
/wd4001
/D_CRT_SECURE_NO_WARNINGS
)
endif()
endif()
option(ENABLE_SANITIZERS "Enables AddressSanitizer and UndefinedBehaviorSanitizer." OFF)
if (ENABLE_SANITIZERS)
list(APPEND custom_compiler_flags
-fno-omit-frame-pointer
-fsanitize=address
-fsanitize=undefined
-fsanitize=float-cast-overflow
-fsanitize-address-use-after-scope
-fsanitize=integer
-01
-fno-sanitize-recover
)
endif()
option(ENABLE_SAFE_STACK "Enables the SafeStack instrumentation pass by the Code Pointer Integrity Project" OFF)
if (ENABLE_SAFE_STACK)
if (ENABLE_SANITIZERS)
message(FATAL_ERROR "ENABLE_SAFE_STACK cannot be used in combination with ENABLE_SANITIZERS")
endif()
list(APPEND custom_compiler_flags
-fsanitize=safe-stack
)
endif()
option(ENABLE_PUBLIC_SYMBOLS "Export library symbols." On)
if (ENABLE_PUBLIC_SYMBOLS)
list(APPEND custom_compiler_flags -fvisibility=hidden)
add_definitions(-DCJSON_EXPORT_SYMBOLS -DCJSON_API_VISIBILITY)
endif()
option(ENABLE_HIDDEN_SYMBOLS "Hide library symbols." Off)
if (ENABLE_HIDDEN_SYMBOLS)
add_definitions(-DCJSON_HIDE_SYMBOLS -UCJSON_API_VISIBILITY)
endif()
# apply custom compiler flags
foreach(compiler_flag ${custom_compiler_flags})
#remove problematic characters
string(REGEX REPLACE "[^a-zA-Z0-9]" "" current_variable ${compiler_flag})
CHECK_C_COMPILER_FLAG(${compiler_flag} "FLAG_SUPPORTED_${current_variable}")
if (FLAG_SUPPORTED_${current_variable})
list(APPEND supported_compiler_flags)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${compiler_flag}")
endif()
endforeach()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${supported_compiler_flags}")
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
option(ENABLE_TARGET_EXPORT "Enable exporting of CMake targets. Disable when it causes problems!" ON)
#cJSON
set(CJSON_LIB cjson)
file(GLOB HEADERS cJSON.h)
set(SOURCES cJSON.c)
option(BUILD_SHARED_AND_STATIC_LIBS "Build both shared and static libraries" Off)
option(CJSON_OVERRIDE_BUILD_SHARED_LIBS "Override BUILD_SHARED_LIBS with CJSON_BUILD_SHARED_LIBS" OFF)
option(CJSON_BUILD_SHARED_LIBS "Overrides BUILD_SHARED_LIBS if CJSON_OVERRIDE_BUILD_SHARED_LIBS is enabled" ON)
option(ENABLE_CJSON_VERSION_SO "Enables cJSON so version" ON)
if ((CJSON_OVERRIDE_BUILD_SHARED_LIBS AND CJSON_BUILD_SHARED_LIBS) OR ((NOT CJSON_OVERRIDE_BUILD_SHARED_LIBS) AND BUILD_SHARED_LIBS))
set(CJSON_LIBRARY_TYPE SHARED)
else()
set(CJSON_LIBRARY_TYPE STATIC)
endif()
if (NOT BUILD_SHARED_AND_STATIC_LIBS)
add_library("${CJSON_LIB}" "${CJSON_LIBRARY_TYPE}" "${HEADERS}" "${SOURCES}")
else()
# See https://cmake.org/Wiki/CMake_FAQ#How_do_I_make_my_shared_and_static_libraries_have_the_same_root_name.2C_but_different_suffixes.3F
add_library("${CJSON_LIB}" SHARED "${HEADERS}" "${SOURCES}")
add_library("${CJSON_LIB}-static" STATIC "${HEADERS}" "${SOURCES}")
set_target_properties("${CJSON_LIB}-static" PROPERTIES OUTPUT_NAME "${CJSON_LIB}")
set_target_properties("${CJSON_LIB}-static" PROPERTIES PREFIX "lib")
endif()
if (NOT WIN32)
target_link_libraries("${CJSON_LIB}" m)
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/library_config/libcjson.pc.in"
"${CMAKE_CURRENT_BINARY_DIR}/libcjson.pc" @ONLY)
install(FILES cJSON.h DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}/cjson")
install (FILES "${CMAKE_CURRENT_BINARY_DIR}/libcjson.pc" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/pkgconfig")
install(TARGETS "${CJSON_LIB}"
EXPORT "${CJSON_LIB}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}"
LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}"
RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}"
INCLUDES DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}"
)
if (BUILD_SHARED_AND_STATIC_LIBS)
install(TARGETS "${CJSON_LIB}-static"
EXPORT "${CJSON_LIB}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}"
INCLUDES DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}"
)
endif()
if(ENABLE_TARGET_EXPORT)
# export library information for CMake projects
install(EXPORT "${CJSON_LIB}" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cJSON")
endif()
if(ENABLE_CJSON_VERSION_SO)
set_target_properties("${CJSON_LIB}"
PROPERTIES
SOVERSION "${CJSON_VERSION_SO}"
VERSION "${PROJECT_VERSION}")
endif()
#cJSON_Utils
option(ENABLE_CJSON_UTILS "Enable building the cJSON_Utils library." OFF)
if(ENABLE_CJSON_UTILS)
set(CJSON_UTILS_LIB cjson_utils)
file(GLOB HEADERS_UTILS cJSON_Utils.h)
set(SOURCES_UTILS cJSON_Utils.c)
if (NOT BUILD_SHARED_AND_STATIC_LIBS)
add_library("${CJSON_UTILS_LIB}" "${CJSON_LIBRARY_TYPE}" "${HEADERS_UTILS}" "${SOURCES_UTILS}")
target_link_libraries("${CJSON_UTILS_LIB}" "${CJSON_LIB}")
else()
add_library("${CJSON_UTILS_LIB}" SHARED "${HEADERS_UTILS}" "${SOURCES_UTILS}")
target_link_libraries("${CJSON_UTILS_LIB}" "${CJSON_LIB}")
add_library("${CJSON_UTILS_LIB}-static" STATIC "${HEADERS_UTILS}" "${SOURCES_UTILS}")
target_link_libraries("${CJSON_UTILS_LIB}-static" "${CJSON_LIB}-static")
set_target_properties("${CJSON_UTILS_LIB}-static" PROPERTIES OUTPUT_NAME "${CJSON_UTILS_LIB}")
set_target_properties("${CJSON_UTILS_LIB}-static" PROPERTIES PREFIX "lib")
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/library_config/libcjson_utils.pc.in"
"${CMAKE_CURRENT_BINARY_DIR}/libcjson_utils.pc" @ONLY)
install(TARGETS "${CJSON_UTILS_LIB}"
EXPORT "${CJSON_UTILS_LIB}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}"
LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}"
RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}"
INCLUDES DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}"
)
if (BUILD_SHARED_AND_STATIC_LIBS)
install(TARGETS "${CJSON_UTILS_LIB}-static"
EXPORT "${CJSON_UTILS_LIB}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}"
INCLUDES DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}"
)
endif()
install(FILES cJSON_Utils.h DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}/cjson")
install (FILES "${CMAKE_CURRENT_BINARY_DIR}/libcjson_utils.pc" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/pkgconfig")
if(ENABLE_TARGET_EXPORT)
# export library information for CMake projects
install(EXPORT "${CJSON_UTILS_LIB}" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cJSON")
endif()
if(ENABLE_CJSON_VERSION_SO)
set_target_properties("${CJSON_UTILS_LIB}"
PROPERTIES
SOVERSION "${CJSON_UTILS_VERSION_SO}"
VERSION "${PROJECT_VERSION}")
endif()
endif()
# create the other package config files
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/library_config/cJSONConfig.cmake.in"
${PROJECT_BINARY_DIR}/cJSONConfig.cmake @ONLY)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/library_config/cJSONConfigVersion.cmake.in"
${PROJECT_BINARY_DIR}/cJSONConfigVersion.cmake @ONLY)
if(ENABLE_TARGET_EXPORT)
# Install package config files
install(FILES ${PROJECT_BINARY_DIR}/cJSONConfig.cmake
${PROJECT_BINARY_DIR}/cJSONConfigVersion.cmake
DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cJSON")
endif()
option(ENABLE_CJSON_TEST "Enable building cJSON test" ON)
if(ENABLE_CJSON_TEST)
enable_testing()
set(TEST_CJSON cJSON_test)
add_executable("${TEST_CJSON}" test.c)
target_link_libraries("${TEST_CJSON}" "${CJSON_LIB}")
add_test(NAME ${TEST_CJSON} COMMAND "${CMAKE_CURRENT_BINARY_DIR}/${TEST_CJSON}")
# Disable -fsanitize=float-divide-by-zero for cJSON_test
if (FLAG_SUPPORTED_fsanitizefloatdividebyzero)
if ("${CMAKE_VERSION}" VERSION_LESS "2.8.12")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-sanitize=float-divide-by-zero")
else()
target_compile_options(${TEST_CJSON} PRIVATE "-fno-sanitize=float-divide-by-zero")
endif()
endif()
#"check" target that automatically builds everything and runs the tests
add_custom_target(check
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
DEPENDS ${TEST_CJSON})
endif()
#Create the uninstall target
option(ENABLE_CJSON_UNINSTALL "Enable creating uninstall target" ON)
if(ENABLE_CJSON_UNINSTALL)
add_custom_target(uninstall "${CMAKE_COMMAND}" -P
"${PROJECT_SOURCE_DIR}/library_config/uninstall.cmake")
endif()
# Enable the use of locales
option(ENABLE_LOCALES "Enable the use of locales" ON)
if(ENABLE_LOCALES)
add_definitions(-DENABLE_LOCALES)
endif()
add_subdirectory(tests)
add_subdirectory(fuzzing)

View File

@ -0,0 +1,79 @@
Contributors
============
Original Author:
- [Dave Gamble](https://github.com/DaveGamble)
Current Maintainer:
- [Max Bruckner](https://github.com/FSMaxB)
- [Alan Wang](https://github.com/Alanscut)
Contributors:
* [Ajay Bhargav](https://github.com/ajaybhargav)
* [Alper Akcan](https://github.com/alperakcan)
* [Andrew Tang](https://github.com/singku)
* [Anton Sergeev](https://github.com/anton-sergeev)
* [Benbuck Nason](https://github.com/bnason-nf)
* [Bernt Johan Damslora](https://github.com/bjda)
* [Bob Kocisko](https://github.com/bobkocisko)
* [Christian Schulze](https://github.com/ChristianSch)
* [Casperinous](https://github.com/Casperinous)
* [ChenYuan](https://github.com/zjuchenyuan)
* [Debora Grosse](https://github.com/DeboraG)
* [dieyushi](https://github.com/dieyushi)
* [Dōngwén Huáng (黄东文)](https://github.com/DongwenHuang)
* [Donough Liu](https://github.com/ldm0)
* [Erez Oxman](https://github.com/erez-o)
* Eswar Yaganti
* [Evan Todd](https://github.com/etodd)
* [Fabrice Fontaine](https://github.com/ffontaine)
* Ian Mobley
* Irwan Djadjadi
* [HuKeping](https://github.com/HuKeping)
* [IvanVoid](https://github.com/npi3pak)
* [Jakub Wilk](https://github.com/jwilk)
* [Jiri Zouhar](https://github.com/loigu)
* [Jonathan Fether](https://github.com/jfether)
* [Julian Ste](https://github.com/julian-st)
* [Julián Vásquez](https://github.com/juvasquezg)
* [Kevin Branigan](https://github.com/kbranigan)
* [Kevin Sapper](https://github.com/sappo)
* [Kyle Chisholm](https://github.com/ChisholmKyle)
* [Linus Wallgren](https://github.com/ecksun)
* [Mateusz Szafoni](https://github.com/raiden00pl)
* Mike Pontillo
* [miaoerduo](https://github.com/miaoerduo)
* [Mike Jerris](https://github.com/mjerris)
* [Mike Robinson](https://github.com/mhrobinson)
* [Moorthy](https://github.com/moorthy-bs)
* [myd7349](https://github.com/myd7349)
* [NancyLi1013](https://github.com/NancyLi1013)
* Paulo Antonio Alvarez
* [Paweł Malowany](https://github.com/PawelMalowany)
* [Pawel Winogrodzki](https://github.com/PawelWMS)
* [prefetchnta](https://github.com/prefetchnta)
* [Rafael Leal Dias](https://github.com/rafaeldias)
* [Randy](https://github.com/randy408)
* [raiden00pl](https://github.com/raiden00pl)
* [Robin Mallinson](https://github.com/rmallins)
* [Rod Vagg](https://github.com/rvagg)
* [Roland Meertens](https://github.com/rmeertens)
* [Romain Porte](https://github.com/MicroJoe)
* [SANJEEV BA](https://github.com/basanjeev)
* [Sang-Heon Jeon](https://github.com/lntuition)
* [Simon Sobisch](https://github.com/GitMensch)
* [Simon Ricaldone](https://github.com/simon-p-r)
* [Square789](https://github.com/Square789)
* [Stephan Gatzka](https://github.com/gatzka)
* [Vemake](https://github.com/vemakereporter)
* [Wei Tan](https://github.com/tan-wei)
* [Weston Schmidt](https://github.com/schmidtw)
* [xiaomianhehe](https://github.com/xiaomianhehe)
* [yangfl](https://github.com/yangfl)
* [yuta-oxo](https://github.com/yuta-oxo)
* [Zach Hindes](https://github.com/zhindes)
* [Zhao Zhixu](https://github.com/zhaozhixu)
And probably more people on [SourceForge](https://sourceforge.net/p/cjson/bugs/search/?q=status%3Aclosed-rejected+or+status%3Aclosed-out-of-date+or+status%3Awont-fix+or+status%3Aclosed-fixed+or+status%3Aclosed&page=0)
Also thanks to all the people who reported bugs and suggested new features.

20
components/cjson/LICENSE Normal file
View File

@ -0,0 +1,20 @@
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

163
components/cjson/Makefile Normal file
View File

@ -0,0 +1,163 @@
CJSON_OBJ = cJSON.o
UTILS_OBJ = cJSON_Utils.o
CJSON_LIBNAME = libcjson
UTILS_LIBNAME = libcjson_utils
CJSON_TEST = cJSON_test
CJSON_TEST_SRC = cJSON.c test.c
LDLIBS = -lm
LIBVERSION = 1.7.15
CJSON_SOVERSION = 1
UTILS_SOVERSION = 1
CJSON_SO_LDFLAG=-Wl,-soname=$(CJSON_LIBNAME).so.$(CJSON_SOVERSION)
UTILS_SO_LDFLAG=-Wl,-soname=$(UTILS_LIBNAME).so.$(UTILS_SOVERSION)
PREFIX ?= /usr/local
INCLUDE_PATH ?= include/cjson
LIBRARY_PATH ?= lib
INSTALL_INCLUDE_PATH = $(DESTDIR)$(PREFIX)/$(INCLUDE_PATH)
INSTALL_LIBRARY_PATH = $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH)
INSTALL ?= cp -a
CC = gcc -std=c89
# validate gcc version for use fstack-protector-strong
MIN_GCC_VERSION = "4.9"
GCC_VERSION := "`$(CC) -dumpversion`"
IS_GCC_ABOVE_MIN_VERSION := $(shell expr "$(GCC_VERSION)" ">=" "$(MIN_GCC_VERSION)")
ifeq "$(IS_GCC_ABOVE_MIN_VERSION)" "1"
CFLAGS += -fstack-protector-strong
else
CFLAGS += -fstack-protector
endif
PIC_FLAGS = -fPIC
R_CFLAGS = $(PIC_FLAGS) -pedantic -Wall -Werror -Wstrict-prototypes -Wwrite-strings -Wshadow -Winit-self -Wcast-align -Wformat=2 -Wmissing-prototypes -Wstrict-overflow=2 -Wcast-qual -Wc++-compat -Wundef -Wswitch-default -Wconversion $(CFLAGS)
uname := $(shell sh -c 'uname -s 2>/dev/null || echo false')
#library file extensions
SHARED = so
STATIC = a
## create dynamic (shared) library on Darwin (base OS for MacOSX and IOS)
ifeq (Darwin, $(uname))
SHARED = dylib
CJSON_SO_LDFLAG = ""
UTILS_SO_LDFLAG = ""
endif
#cJSON library names
CJSON_SHARED = $(CJSON_LIBNAME).$(SHARED)
CJSON_SHARED_VERSION = $(CJSON_LIBNAME).$(SHARED).$(LIBVERSION)
CJSON_SHARED_SO = $(CJSON_LIBNAME).$(SHARED).$(CJSON_SOVERSION)
CJSON_STATIC = $(CJSON_LIBNAME).$(STATIC)
#cJSON_Utils library names
UTILS_SHARED = $(UTILS_LIBNAME).$(SHARED)
UTILS_SHARED_VERSION = $(UTILS_LIBNAME).$(SHARED).$(LIBVERSION)
UTILS_SHARED_SO = $(UTILS_LIBNAME).$(SHARED).$(UTILS_SOVERSION)
UTILS_STATIC = $(UTILS_LIBNAME).$(STATIC)
SHARED_CMD = $(CC) -shared -o
.PHONY: all shared static tests clean install
all: shared static tests
shared: $(CJSON_SHARED) $(UTILS_SHARED)
static: $(CJSON_STATIC) $(UTILS_STATIC)
tests: $(CJSON_TEST)
test: tests
./$(CJSON_TEST)
.c.o:
$(CC) -c $(R_CFLAGS) $<
#tests
#cJSON
$(CJSON_TEST): $(CJSON_TEST_SRC) cJSON.h
$(CC) $(R_CFLAGS) $(CJSON_TEST_SRC) -o $@ $(LDLIBS) -I.
#static libraries
#cJSON
$(CJSON_STATIC): $(CJSON_OBJ)
$(AR) rcs $@ $<
#cJSON_Utils
$(UTILS_STATIC): $(UTILS_OBJ)
$(AR) rcs $@ $<
#shared libraries .so.1.0.0
#cJSON
$(CJSON_SHARED_VERSION): $(CJSON_OBJ)
$(CC) -shared -o $@ $< $(CJSON_SO_LDFLAG) $(LDFLAGS)
#cJSON_Utils
$(UTILS_SHARED_VERSION): $(UTILS_OBJ)
$(CC) -shared -o $@ $< $(CJSON_OBJ) $(UTILS_SO_LDFLAG) $(LDFLAGS)
#objects
#cJSON
$(CJSON_OBJ): cJSON.c cJSON.h
#cJSON_Utils
$(UTILS_OBJ): cJSON_Utils.c cJSON_Utils.h cJSON.h
#links .so -> .so.1 -> .so.1.0.0
#cJSON
$(CJSON_SHARED_SO): $(CJSON_SHARED_VERSION)
ln -s $(CJSON_SHARED_VERSION) $(CJSON_SHARED_SO)
$(CJSON_SHARED): $(CJSON_SHARED_SO)
ln -s $(CJSON_SHARED_SO) $(CJSON_SHARED)
#cJSON_Utils
$(UTILS_SHARED_SO): $(UTILS_SHARED_VERSION)
ln -s $(UTILS_SHARED_VERSION) $(UTILS_SHARED_SO)
$(UTILS_SHARED): $(UTILS_SHARED_SO)
ln -s $(UTILS_SHARED_SO) $(UTILS_SHARED)
#install
#cJSON
install-cjson:
mkdir -p $(INSTALL_LIBRARY_PATH) $(INSTALL_INCLUDE_PATH)
$(INSTALL) cJSON.h $(INSTALL_INCLUDE_PATH)
$(INSTALL) $(CJSON_SHARED) $(CJSON_SHARED_SO) $(CJSON_SHARED_VERSION) $(INSTALL_LIBRARY_PATH)
#cJSON_Utils
install-utils: install-cjson
$(INSTALL) cJSON_Utils.h $(INSTALL_INCLUDE_PATH)
$(INSTALL) $(UTILS_SHARED) $(UTILS_SHARED_SO) $(UTILS_SHARED_VERSION) $(INSTALL_LIBRARY_PATH)
install: install-cjson install-utils
#uninstall
#cJSON
uninstall-cjson: uninstall-utils
$(RM) $(INSTALL_LIBRARY_PATH)/$(CJSON_SHARED)
$(RM) $(INSTALL_LIBRARY_PATH)/$(CJSON_SHARED_VERSION)
$(RM) $(INSTALL_LIBRARY_PATH)/$(CJSON_SHARED_SO)
$(RM) $(INSTALL_INCLUDE_PATH)/cJSON.h
#cJSON_Utils
uninstall-utils:
$(RM) $(INSTALL_LIBRARY_PATH)/$(UTILS_SHARED)
$(RM) $(INSTALL_LIBRARY_PATH)/$(UTILS_SHARED_VERSION)
$(RM) $(INSTALL_LIBRARY_PATH)/$(UTILS_SHARED_SO)
$(RM) $(INSTALL_INCLUDE_PATH)/cJSON_Utils.h
remove-dir:
$(if $(wildcard $(INSTALL_LIBRARY_PATH)/*.*),,rmdir $(INSTALL_LIBRARY_PATH))
$(if $(wildcard $(INSTALL_INCLUDE_PATH)/*.*),,rmdir $(INSTALL_INCLUDE_PATH))
uninstall: uninstall-utils uninstall-cjson remove-dir
clean:
$(RM) $(CJSON_OBJ) $(UTILS_OBJ) #delete object files
$(RM) $(CJSON_SHARED) $(CJSON_SHARED_VERSION) $(CJSON_SHARED_SO) $(CJSON_STATIC) #delete cJSON
$(RM) $(UTILS_SHARED) $(UTILS_SHARED_VERSION) $(UTILS_SHARED_SO) $(UTILS_STATIC) #delete cJSON_Utils
$(RM) $(CJSON_TEST) #delete test

572
components/cjson/README.md Normal file
View File

@ -0,0 +1,572 @@
# cJSON
Ultralightweight JSON parser in ANSI C.
## Table of contents
* [License](#license)
* [Usage](#usage)
* [Welcome to cJSON](#welcome-to-cjson)
* [Building](#building)
* [Copying the source](#copying-the-source)
* [CMake](#cmake)
* [Makefile](#makefile)
* [Vcpkg](#Vcpkg)
* [Including cJSON](#including-cjson)
* [Data Structure](#data-structure)
* [Working with the data structure](#working-with-the-data-structure)
* [Basic types](#basic-types)
* [Arrays](#arrays)
* [Objects](#objects)
* [Parsing JSON](#parsing-json)
* [Printing JSON](#printing-json)
* [Example](#example)
* [Printing](#printing)
* [Parsing](#parsing)
* [Caveats](#caveats)
* [Zero Character](#zero-character)
* [Character Encoding](#character-encoding)
* [C Standard](#c-standard)
* [Floating Point Numbers](#floating-point-numbers)
* [Deep Nesting Of Arrays And Objects](#deep-nesting-of-arrays-and-objects)
* [Thread Safety](#thread-safety)
* [Case Sensitivity](#case-sensitivity)
* [Duplicate Object Members](#duplicate-object-members)
* [Enjoy cJSON!](#enjoy-cjson)
## License
MIT License
> Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.
## Usage
### Welcome to cJSON.
cJSON aims to be the dumbest possible parser that you can get your job done with.
It's a single file of C, and a single header file.
JSON is described best here: http://www.json.org/
It's like XML, but fat-free. You use it to move data around, store things, or just
generally represent your program's state.
As a library, cJSON exists to take away as much legwork as it can, but not get in your way.
As a point of pragmatism (i.e. ignoring the truth), I'm going to say that you can use it
in one of two modes: Auto and Manual. Let's have a quick run-through.
I lifted some JSON from this page: http://www.json.org/fatfree.html
That page inspired me to write cJSON, which is a parser that tries to share the same
philosophy as JSON itself. Simple, dumb, out of the way.
### Building
There are several ways to incorporate cJSON into your project.
#### copying the source
Because the entire library is only one C file and one header file, you can just copy `cJSON.h` and `cJSON.c` to your projects source and start using it.
cJSON is written in ANSI C (C89) in order to support as many platforms and compilers as possible.
#### CMake
With CMake, cJSON supports a full blown build system. This way you get the most features. CMake with an equal or higher version than 2.8.5 is supported. With CMake it is recommended to do an out of tree build, meaning the compiled files are put in a directory separate from the source files. So in order to build cJSON with CMake on a Unix platform, make a `build` directory and run CMake inside it.
```
mkdir build
cd build
cmake ..
```
This will create a Makefile and a bunch of other files. You can then compile it:
```
make
```
And install it with `make install` if you want. By default it installs the headers `/usr/local/include/cjson` and the libraries to `/usr/local/lib`. It also installs files for pkg-config to make it easier to detect and use an existing installation of CMake. And it installs CMake config files, that can be used by other CMake based projects to discover the library.
You can change the build process with a list of different options that you can pass to CMake. Turn them on with `On` and off with `Off`:
* `-DENABLE_CJSON_TEST=On`: Enable building the tests. (on by default)
* `-DENABLE_CJSON_UTILS=On`: Enable building cJSON_Utils. (off by default)
* `-DENABLE_TARGET_EXPORT=On`: Enable the export of CMake targets. Turn off if it makes problems. (on by default)
* `-DENABLE_CUSTOM_COMPILER_FLAGS=On`: Enable custom compiler flags (currently for Clang, GCC and MSVC). Turn off if it makes problems. (on by default)
* `-DENABLE_VALGRIND=On`: Run tests with [valgrind](http://valgrind.org). (off by default)
* `-DENABLE_SANITIZERS=On`: Compile cJSON with [AddressSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer) and [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) enabled (if possible). (off by default)
* `-DENABLE_SAFE_STACK`: Enable the [SafeStack](https://clang.llvm.org/docs/SafeStack.html) instrumentation pass. Currently only works with the Clang compiler. (off by default)
* `-DBUILD_SHARED_LIBS=On`: Build the shared libraries. (on by default)
* `-DBUILD_SHARED_AND_STATIC_LIBS=On`: Build both shared and static libraries. (off by default)
* `-DCMAKE_INSTALL_PREFIX=/usr`: Set a prefix for the installation.
* `-DENABLE_LOCALES=On`: Enable the usage of localeconv method. ( on by default )
* `-DCJSON_OVERRIDE_BUILD_SHARED_LIBS=On`: Enable overriding the value of `BUILD_SHARED_LIBS` with `-DCJSON_BUILD_SHARED_LIBS`.
* `-DENABLE_CJSON_VERSION_SO`: Enable cJSON so version. ( on by default )
If you are packaging cJSON for a distribution of Linux, you would probably take these steps for example:
```
mkdir build
cd build
cmake .. -DENABLE_CJSON_UTILS=On -DENABLE_CJSON_TEST=Off -DCMAKE_INSTALL_PREFIX=/usr
make
make DESTDIR=$pkgdir install
```
On Windows CMake is usually used to create a Visual Studio solution file by running it inside the Developer Command Prompt for Visual Studio, for exact steps follow the official documentation from CMake and Microsoft and use the online search engine of your choice. The descriptions of the the options above still generally apply, although not all of them work on Windows.
#### Makefile
**NOTE:** This Method is deprecated. Use CMake if at all possible. Makefile support is limited to fixing bugs.
If you don't have CMake available, but still have GNU make. You can use the makefile to build cJSON:
Run this command in the directory with the source code and it will automatically compile static and shared libraries and a little test program (not the full test suite).
```
make all
```
If you want, you can install the compiled library to your system using `make install`. By default it will install the headers in `/usr/local/include/cjson` and the libraries in `/usr/local/lib`. But you can change this behavior by setting the `PREFIX` and `DESTDIR` variables: `make PREFIX=/usr DESTDIR=temp install`. And uninstall them with: `make PREFIX=/usr DESTDIR=temp uninstall`.
#### Vcpkg
You can download and install cJSON using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
```
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
vcpkg install cjson
```
The cJSON port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
### Including cJSON
If you installed it via CMake or the Makefile, you can include cJSON like this:
```c
#include <cjson/cJSON.h>
```
### Data Structure
cJSON represents JSON data using the `cJSON` struct data type:
```c
/* The cJSON structure: */
typedef struct cJSON
{
struct cJSON *next;
struct cJSON *prev;
struct cJSON *child;
int type;
char *valuestring;
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
int valueint;
double valuedouble;
char *string;
} cJSON;
```
An item of this type represents a JSON value. The type is stored in `type` as a bit-flag (**this means that you cannot find out the type by just comparing the value of `type`**).
To check the type of an item, use the corresponding `cJSON_Is...` function. It does a `NULL` check followed by a type check and returns a boolean value if the item is of this type.
The type can be one of the following:
* `cJSON_Invalid` (check with `cJSON_IsInvalid`): Represents an invalid item that doesn't contain any value. You automatically have this type if you set the item to all zero bytes.
* `cJSON_False` (check with `cJSON_IsFalse`): Represents a `false` boolean value. You can also check for boolean values in general with `cJSON_IsBool`.
* `cJSON_True` (check with `cJSON_IsTrue`): Represents a `true` boolean value. You can also check for boolean values in general with `cJSON_IsBool`.
* `cJSON_NULL` (check with `cJSON_IsNull`): Represents a `null` value.
* `cJSON_Number` (check with `cJSON_IsNumber`): Represents a number value. The value is stored as a double in `valuedouble` and also in `valueint`. If the number is outside of the range of an integer, `INT_MAX` or `INT_MIN` are used for `valueint`.
* `cJSON_String` (check with `cJSON_IsString`): Represents a string value. It is stored in the form of a zero terminated string in `valuestring`.
* `cJSON_Array` (check with `cJSON_IsArray`): Represent an array value. This is implemented by pointing `child` to a linked list of `cJSON` items that represent the values in the array. The elements are linked together using `next` and `prev`, where the first element has `prev.next == NULL` and the last element `next == NULL`.
* `cJSON_Object` (check with `cJSON_IsObject`): Represents an object value. Objects are stored same way as an array, the only difference is that the items in the object store their keys in `string`.
* `cJSON_Raw` (check with `cJSON_IsRaw`): Represents any kind of JSON that is stored as a zero terminated array of characters in `valuestring`. This can be used, for example, to avoid printing the same static JSON over and over again to save performance. cJSON will never create this type when parsing. Also note that cJSON doesn't check if it is valid JSON.
Additionally there are the following two flags:
* `cJSON_IsReference`: Specifies that the item that `child` points to and/or `valuestring` is not owned by this item, it is only a reference. So `cJSON_Delete` and other functions will only deallocate this item, not its `child`/`valuestring`.
* `cJSON_StringIsConst`: This means that `string` points to a constant string. This means that `cJSON_Delete` and other functions will not try to deallocate `string`.
### Working with the data structure
For every value type there is a `cJSON_Create...` function that can be used to create an item of that type.
All of these will allocate a `cJSON` struct that can later be deleted with `cJSON_Delete`.
Note that you have to delete them at some point, otherwise you will get a memory leak.
**Important**: If you have added an item to an array or an object already, you **mustn't** delete it with `cJSON_Delete`. Adding it to an array or object transfers its ownership so that when that array or object is deleted,
it gets deleted as well. You also could use `cJSON_SetValuestring` to change a `cJSON_String`'s `valuestring`, and you needn't to free the previous `valuestring` manually.
#### Basic types
* **null** is created with `cJSON_CreateNull`
* **booleans** are created with `cJSON_CreateTrue`, `cJSON_CreateFalse` or `cJSON_CreateBool`
* **numbers** are created with `cJSON_CreateNumber`. This will set both `valuedouble` and `valueint`. If the number is outside of the range of an integer, `INT_MAX` or `INT_MIN` are used for `valueint`
* **strings** are created with `cJSON_CreateString` (copies the string) or with `cJSON_CreateStringReference` (directly points to the string. This means that `valuestring` won't be deleted by `cJSON_Delete` and you are responsible for its lifetime, useful for constants)
#### Arrays
You can create an empty array with `cJSON_CreateArray`. `cJSON_CreateArrayReference` can be used to create an array that doesn't "own" its content, so its content doesn't get deleted by `cJSON_Delete`.
To add items to an array, use `cJSON_AddItemToArray` to append items to the end.
Using `cJSON_AddItemReferenceToArray` an element can be added as a reference to another item, array or string. This means that `cJSON_Delete` will not delete that items `child` or `valuestring` properties, so no double frees are occurring if they are already used elsewhere.
To insert items in the middle, use `cJSON_InsertItemInArray`. It will insert an item at the given 0 based index and shift all the existing items to the right.
If you want to take an item out of an array at a given index and continue using it, use `cJSON_DetachItemFromArray`, it will return the detached item, so be sure to assign it to a pointer, otherwise you will have a memory leak.
Deleting items is done with `cJSON_DeleteItemFromArray`. It works like `cJSON_DetachItemFromArray`, but deletes the detached item via `cJSON_Delete`.
You can also replace an item in an array in place. Either with `cJSON_ReplaceItemInArray` using an index or with `cJSON_ReplaceItemViaPointer` given a pointer to an element. `cJSON_ReplaceItemViaPointer` will return `0` if it fails. What this does internally is to detach the old item, delete it and insert the new item in its place.
To get the size of an array, use `cJSON_GetArraySize`. Use `cJSON_GetArrayItem` to get an element at a given index.
Because an array is stored as a linked list, iterating it via index is inefficient (`O(n²)`), so you can iterate over an array using the `cJSON_ArrayForEach` macro in `O(n)` time complexity.
#### Objects
You can create an empty object with `cJSON_CreateObject`. `cJSON_CreateObjectReference` can be used to create an object that doesn't "own" its content, so its content doesn't get deleted by `cJSON_Delete`.
To add items to an object, use `cJSON_AddItemToObject`. Use `cJSON_AddItemToObjectCS` to add an item to an object with a name that is a constant or reference (key of the item, `string` in the `cJSON` struct), so that it doesn't get freed by `cJSON_Delete`.
Using `cJSON_AddItemReferenceToArray` an element can be added as a reference to another object, array or string. This means that `cJSON_Delete` will not delete that items `child` or `valuestring` properties, so no double frees are occurring if they are already used elsewhere.
If you want to take an item out of an object, use `cJSON_DetachItemFromObjectCaseSensitive`, it will return the detached item, so be sure to assign it to a pointer, otherwise you will have a memory leak.
Deleting items is done with `cJSON_DeleteItemFromObjectCaseSensitive`. It works like `cJSON_DetachItemFromObjectCaseSensitive` followed by `cJSON_Delete`.
You can also replace an item in an object in place. Either with `cJSON_ReplaceItemInObjectCaseSensitive` using a key or with `cJSON_ReplaceItemViaPointer` given a pointer to an element. `cJSON_ReplaceItemViaPointer` will return `0` if it fails. What this does internally is to detach the old item, delete it and insert the new item in its place.
To get the size of an object, you can use `cJSON_GetArraySize`, this works because internally objects are stored as arrays.
If you want to access an item in an object, use `cJSON_GetObjectItemCaseSensitive`.
To iterate over an object, you can use the `cJSON_ArrayForEach` macro the same way as for arrays.
cJSON also provides convenient helper functions for quickly creating a new item and adding it to an object, like `cJSON_AddNullToObject`. They return a pointer to the new item or `NULL` if they failed.
### Parsing JSON
Given some JSON in a zero terminated string, you can parse it with `cJSON_Parse`.
```c
cJSON *json = cJSON_Parse(string);
```
Given some JSON in a string (whether zero terminated or not), you can parse it with `cJSON_ParseWithLength`.
```c
cJSON *json = cJSON_ParseWithLength(string, buffer_length);
```
It will parse the JSON and allocate a tree of `cJSON` items that represents it. Once it returns, you are fully responsible for deallocating it after use with `cJSON_Delete`.
The allocator used by `cJSON_Parse` is `malloc` and `free` by default but can be changed (globally) with `cJSON_InitHooks`.
If an error occurs a pointer to the position of the error in the input string can be accessed using `cJSON_GetErrorPtr`. Note though that this can produce race conditions in multithreading scenarios, in that case it is better to use `cJSON_ParseWithOpts` with `return_parse_end`.
By default, characters in the input string that follow the parsed JSON will not be considered as an error.
If you want more options, use `cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)`.
`return_parse_end` returns a pointer to the end of the JSON in the input string or the position that an error occurs at (thereby replacing `cJSON_GetErrorPtr` in a thread safe way). `require_null_terminated`, if set to `1` will make it an error if the input string contains data after the JSON.
If you want more options giving buffer length, use `cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)`.
### Printing JSON
Given a tree of `cJSON` items, you can print them as a string using `cJSON_Print`.
```c
char *string = cJSON_Print(json);
```
It will allocate a string and print a JSON representation of the tree into it. Once it returns, you are fully responsible for deallocating it after use with your allocator. (usually `free`, depends on what has been set with `cJSON_InitHooks`).
`cJSON_Print` will print with whitespace for formatting. If you want to print without formatting, use `cJSON_PrintUnformatted`.
If you have a rough idea of how big your resulting string will be, you can use `cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)`. `fmt` is a boolean to turn formatting with whitespace on and off. `prebuffer` specifies the first buffer size to use for printing. `cJSON_Print` currently uses 256 bytes for its first buffer size. Once printing runs out of space, a new buffer is allocated and the old gets copied over before printing is continued.
These dynamic buffer allocations can be completely avoided by using `cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)`. It takes a buffer to a pointer to print to and its length. If the length is reached, printing will fail and it returns `0`. In case of success, `1` is returned. Note that you should provide 5 bytes more than is actually needed, because cJSON is not 100% accurate in estimating if the provided memory is enough.
### Example
In this example we want to build and parse the following JSON:
```json
{
"name": "Awesome 4K",
"resolutions": [
{
"width": 1280,
"height": 720
},
{
"width": 1920,
"height": 1080
},
{
"width": 3840,
"height": 2160
}
]
}
```
#### Printing
Let's build the above JSON and print it to a string:
```c
//create a monitor with a list of supported resolutions
//NOTE: Returns a heap allocated string, you are required to free it after use.
char *create_monitor(void)
{
const unsigned int resolution_numbers[3][2] = {
{1280, 720},
{1920, 1080},
{3840, 2160}
};
char *string = NULL;
cJSON *name = NULL;
cJSON *resolutions = NULL;
cJSON *resolution = NULL;
cJSON *width = NULL;
cJSON *height = NULL;
size_t index = 0;
cJSON *monitor = cJSON_CreateObject();
if (monitor == NULL)
{
goto end;
}
name = cJSON_CreateString("Awesome 4K");
if (name == NULL)
{
goto end;
}
/* after creation was successful, immediately add it to the monitor,
* thereby transferring ownership of the pointer to it */
cJSON_AddItemToObject(monitor, "name", name);
resolutions = cJSON_CreateArray();
if (resolutions == NULL)
{
goto end;
}
cJSON_AddItemToObject(monitor, "resolutions", resolutions);
for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index)
{
resolution = cJSON_CreateObject();
if (resolution == NULL)
{
goto end;
}
cJSON_AddItemToArray(resolutions, resolution);
width = cJSON_CreateNumber(resolution_numbers[index][0]);
if (width == NULL)
{
goto end;
}
cJSON_AddItemToObject(resolution, "width", width);
height = cJSON_CreateNumber(resolution_numbers[index][1]);
if (height == NULL)
{
goto end;
}
cJSON_AddItemToObject(resolution, "height", height);
}
string = cJSON_Print(monitor);
if (string == NULL)
{
fprintf(stderr, "Failed to print monitor.\n");
}
end:
cJSON_Delete(monitor);
return string;
}
```
Alternatively we can use the `cJSON_Add...ToObject` helper functions to make our lives a little easier:
```c
//NOTE: Returns a heap allocated string, you are required to free it after use.
char *create_monitor_with_helpers(void)
{
const unsigned int resolution_numbers[3][2] = {
{1280, 720},
{1920, 1080},
{3840, 2160}
};
char *string = NULL;
cJSON *resolutions = NULL;
size_t index = 0;
cJSON *monitor = cJSON_CreateObject();
if (cJSON_AddStringToObject(monitor, "name", "Awesome 4K") == NULL)
{
goto end;
}
resolutions = cJSON_AddArrayToObject(monitor, "resolutions");
if (resolutions == NULL)
{
goto end;
}
for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index)
{
cJSON *resolution = cJSON_CreateObject();
if (cJSON_AddNumberToObject(resolution, "width", resolution_numbers[index][0]) == NULL)
{
goto end;
}
if (cJSON_AddNumberToObject(resolution, "height", resolution_numbers[index][1]) == NULL)
{
goto end;
}
cJSON_AddItemToArray(resolutions, resolution);
}
string = cJSON_Print(monitor);
if (string == NULL)
{
fprintf(stderr, "Failed to print monitor.\n");
}
end:
cJSON_Delete(monitor);
return string;
}
```
#### Parsing
In this example we will parse a JSON in the above format and check if the monitor supports a Full HD resolution while printing some diagnostic output:
```c
/* return 1 if the monitor supports full hd, 0 otherwise */
int supports_full_hd(const char * const monitor)
{
const cJSON *resolution = NULL;
const cJSON *resolutions = NULL;
const cJSON *name = NULL;
int status = 0;
cJSON *monitor_json = cJSON_Parse(monitor);
if (monitor_json == NULL)
{
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL)
{
fprintf(stderr, "Error before: %s\n", error_ptr);
}
status = 0;
goto end;
}
name = cJSON_GetObjectItemCaseSensitive(monitor_json, "name");
if (cJSON_IsString(name) && (name->valuestring != NULL))
{
printf("Checking monitor \"%s\"\n", name->valuestring);
}
resolutions = cJSON_GetObjectItemCaseSensitive(monitor_json, "resolutions");
cJSON_ArrayForEach(resolution, resolutions)
{
cJSON *width = cJSON_GetObjectItemCaseSensitive(resolution, "width");
cJSON *height = cJSON_GetObjectItemCaseSensitive(resolution, "height");
if (!cJSON_IsNumber(width) || !cJSON_IsNumber(height))
{
status = 0;
goto end;
}
if ((width->valuedouble == 1920) && (height->valuedouble == 1080))
{
status = 1;
goto end;
}
}
end:
cJSON_Delete(monitor_json);
return status;
}
```
Note that there are no NULL checks except for the result of `cJSON_Parse` because `cJSON_GetObjectItemCaseSensitive` checks for `NULL` inputs already, so a `NULL` value is just propagated and `cJSON_IsNumber` and `cJSON_IsString` return `0` if the input is `NULL`.
### Caveats
#### Zero Character
cJSON doesn't support strings that contain the zero character `'\0'` or `\u0000`. This is impossible with the current API because strings are zero terminated.
#### Character Encoding
cJSON only supports UTF-8 encoded input. In most cases it doesn't reject invalid UTF-8 as input though, it just propagates it through as is. As long as the input doesn't contain invalid UTF-8, the output will always be valid UTF-8.
#### C Standard
cJSON is written in ANSI C (or C89, C90). If your compiler or C library doesn't follow this standard, correct behavior is not guaranteed.
NOTE: ANSI C is not C++ therefore it shouldn't be compiled with a C++ compiler. You can compile it with a C compiler and link it with your C++ code however. Although compiling with a C++ compiler might work, correct behavior is not guaranteed.
#### Floating Point Numbers
cJSON does not officially support any `double` implementations other than IEEE754 double precision floating point numbers. It might still work with other implementations but bugs with these will be considered invalid.
The maximum length of a floating point literal that cJSON supports is currently 63 characters.
#### Deep Nesting Of Arrays And Objects
cJSON doesn't support arrays and objects that are nested too deeply because this would result in a stack overflow. To prevent this cJSON limits the depth to `CJSON_NESTING_LIMIT` which is 1000 by default but can be changed at compile time.
#### Thread Safety
In general cJSON is **not thread safe**.
However it is thread safe under the following conditions:
* `cJSON_GetErrorPtr` is never used (the `return_parse_end` parameter of `cJSON_ParseWithOpts` can be used instead)
* `cJSON_InitHooks` is only ever called before using cJSON in any threads.
* `setlocale` is never called before all calls to cJSON functions have returned.
#### Case Sensitivity
When cJSON was originally created, it didn't follow the JSON standard and didn't make a distinction between uppercase and lowercase letters. If you want the correct, standard compliant, behavior, you need to use the `CaseSensitive` functions where available.
#### Duplicate Object Members
cJSON supports parsing and printing JSON that contains objects that have multiple members with the same name. `cJSON_GetObjectItemCaseSensitive` however will always only return the first one.
# Enjoy cJSON!
- Dave Gamble (original author)
- Max Bruckner and Alan Wang (current maintainer)
- and the other [cJSON contributors](CONTRIBUTORS.md)

View File

@ -0,0 +1,86 @@
os: Visual Studio 2015
# ENABLE_CUSTOM_COMPILER_FLAGS - on by default
# ENABLE_SANITIZERS - off by default
# ENABLE_PUBLIC_SYMBOLS - on by default
# BUILD_SHARED_LIBS - on by default
# ENABLE_TARGET_EXPORT - on by default
# ENABLE_CJSON_UTILS - off by default
# ENABLE_CJSON_TEST -on by default
# ENABLE_VALGRIND - off by default
# ENABLE_FUZZING - off by default
environment:
matrix:
- GENERATOR: "Visual Studio 14 2015"
BUILD_SHARED_LIBS: ON
ENABLE_CJSON_TEST: OFF
ENABLE_CJSON_UTILS: ON
- GENERATOR: "Visual Studio 14 2015"
BUILD_SHARED_LIBS: OFF
ENABLE_CJSON_TEST: OFF
ENABLE_CJSON_UTILS: ON
- GENERATOR: "Visual Studio 12 2013"
BUILD_SHARED_LIBS: ON
ENABLE_CJSON_TEST: OFF
ENABLE_CJSON_UTILS: ON
- GENERATOR: "Visual Studio 12 2013"
BUILD_SHARED_LIBS: OFF
ENABLE_CJSON_TEST: OFF
ENABLE_CJSON_UTILS: ON
- GENERATOR: "Visual Studio 11 2012"
BUILD_SHARED_LIBS: ON
ENABLE_CJSON_TEST: OFF
ENABLE_CJSON_UTILS: ON
- GENERATOR: "Visual Studio 11 2012"
BUILD_SHARED_LIBS: OFF
ENABLE_CJSON_TEST: OFF
ENABLE_CJSON_UTILS: ON
- GENERATOR: "Visual Studio 10 2010"
BUILD_SHARED_LIBS: ON
ENABLE_CJSON_TEST: OFF
ENABLE_CJSON_UTILS: ON
- GENERATOR: "Visual Studio 10 2010"
BUILD_SHARED_LIBS: OFF
ENABLE_CJSON_TEST: OFF
ENABLE_CJSON_UTILS: ON
- GENERATOR: "Visual Studio 9 2008"
BUILD_SHARED_LIBS: ON
ENABLE_CJSON_TEST: OFF
ENABLE_CJSON_UTILS: ON
- GENERATOR: "Visual Studio 9 2008"
BUILD_SHARED_LIBS: OFF
ENABLE_CJSON_TEST: OFF
ENABLE_CJSON_UTILS: ON
platform:
- x86
- x64
matrix:
exclude:
- platform: x64
GENERATOR: "Visual Studio 9 2008"
configuration:
- Release
build_script:
- ps: if($env:PLATFORM -eq "x64") { $env:CMAKE_GEN_SUFFIX=" Win64" }
- cmake "-G%GENERATOR%%CMAKE_GEN_SUFFIX%" -DBUILD_SHARED_LIBS=%BUILD_SHARED_LIBS% -DENABLE_CJSON_TEST=%ENABLE_CJSON_TEST% -H. -Bbuild
- cmake --build build --config "%CONFIGURATION%"
on_failure:
- ps: if(Test-Path builds/CMakeFiles/CMakeOutput.log) { cat builds/CMakeFiles/CMakeOutput.log }
- ps: if(Test-Path builds/CMakeFiles/CMakeError.log) { cat builds/CMakeFiles/CMakeError.log }

3119
components/cjson/cJSON.c Normal file

File diff suppressed because it is too large Load Diff

300
components/cjson/cJSON.h Normal file
View File

@ -0,0 +1,300 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef cJSON__h
#define cJSON__h
#ifdef __cplusplus
extern "C"
{
#endif
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
#define __WINDOWS__
#endif
#ifdef __WINDOWS__
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
For *nix builds that support visibility attribute, you can define similar behavior by
setting default visibility to hidden by adding
-fvisibility=hidden (for gcc)
or
-xldscope=hidden (for sun cc)
to CFLAGS
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
*/
#define CJSON_CDECL __cdecl
#define CJSON_STDCALL __stdcall
/* export symbols by default, this is necessary for copy pasting the C and header file */
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_EXPORT_SYMBOLS
#endif
#if defined(CJSON_HIDE_SYMBOLS)
#define CJSON_PUBLIC(type) type CJSON_STDCALL
#elif defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
#elif defined(CJSON_IMPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
#endif
#else /* !__WINDOWS__ */
#define CJSON_CDECL
#define CJSON_STDCALL
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
#else
#define CJSON_PUBLIC(type) type
#endif
#endif
/* project version */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 7
#define CJSON_VERSION_PATCH 15
#include <stddef.h>
/* cJSON Types: */
#define cJSON_Invalid (0)
#define cJSON_False (1 << 0)
#define cJSON_True (1 << 1)
#define cJSON_NULL (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw (1 << 7) /* raw json */
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
/* The cJSON structure: */
typedef struct cJSON
{
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *next;
struct cJSON *prev;
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
struct cJSON *child;
/* The type of the item, as above. */
int type;
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
char *valuestring;
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
int valueint;
/* The item's number, if type==cJSON_Number */
double valuedouble;
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
char *string;
} cJSON;
typedef struct cJSON_Hooks
{
/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
void *(CJSON_CDECL *malloc_fn)(size_t sz);
void (CJSON_CDECL *free_fn)(void *ptr);
} cJSON_Hooks;
typedef int cJSON_bool;
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
* This is to prevent stack overflows. */
#ifndef CJSON_NESTING_LIMIT
#define CJSON_NESTING_LIMIT 1000
#endif
/* returns the version of cJSON as a string */
CJSON_PUBLIC(const char*) cJSON_Version(void);
/* Supply malloc, realloc and free functions to cJSON */
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
/* Render a cJSON entity to text for transfer/storage. */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. */
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
/* Delete a cJSON entity and all subentities. */
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
/* Returns the number of items in an array (or object). */
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
/* Get item "string" from object. Case insensitive. */
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
/* Check item type and return its value */
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
/* These functions check the type of an item */
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
/* These calls create a cJSON item of the appropriate type. */
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
/* raw json */
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
/* Create a string where valuestring references a string so
* it will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
/* Create an object/array that only references it's elements so
* they will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
/* These utilities create an Array of count items.
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
/* Append item to the specified array/object. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
* writing to `item->string` */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
/* Remove/Detach items from Arrays/Objects. */
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
/* Update array items. */
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
* need to be released. With recurse!=0, it will duplicate any children connected to the item.
* The item->next and ->prev pointers are always zero on return from Duplicate. */
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
* The input pointer json cannot point to a read-only address area, such as a string constant,
* but should point to a readable and writable address area. */
CJSON_PUBLIC(void) cJSON_Minify(char *json);
/* Helper functions for creating and adding items to an object at the same time.
* They return the added item or NULL on failure. */
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
/* helper for the cJSON_SetNumberValue macro */
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
#define cJSON_SetBoolValue(object, boolValue) ( \
(object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
(object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
cJSON_Invalid\
)
/* Macro for iterating over an array or object */
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
CJSON_PUBLIC(void) cJSON_free(void *object);
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef cJSON_Utils__h
#define cJSON_Utils__h
#ifdef __cplusplus
extern "C"
{
#endif
#include "cJSON.h"
/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer);
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer);
/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */
/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to);
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to);
/* Utility for generating patch array entries. */
CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value);
/* Returns 0 for success. */
CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches);
CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches);
/*
// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use:
//int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches)
//{
// cJSON *modme = cJSON_Duplicate(*object, 1);
// int error = cJSONUtils_ApplyPatches(modme, patches);
// if (!error)
// {
// cJSON_Delete(*object);
// *object = modme;
// }
// else
// {
// cJSON_Delete(modme);
// }
//
// return error;
//}
// Code not added to library since this strategy is a LOT slower.
*/
/* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */
/* target will be modified by patch. return value is new ptr for target. */
CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch);
CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch);
/* generates a patch to move from -> to */
/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to);
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to);
/* Given a root object and a target object, construct a pointer from one to the other. */
CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target);
/* Sorts the members of the object into alphabetical order. */
CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object);
CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object);
#ifdef __cplusplus
}
#endif
#endif

1
components/cjson/fuzzing/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
afl-build

View File

@ -0,0 +1,34 @@
option(ENABLE_FUZZING "Create executables and targets for fuzzing cJSON with afl." Off)
if (ENABLE_FUZZING)
find_program(AFL_FUZZ afl-fuzz)
if ("${AFL_FUZZ}" MATCHES "AFL_FUZZ-NOTFOUND")
message(FATAL_ERROR "Couldn't find afl-fuzz.")
endif()
add_executable(afl-main afl.c)
target_link_libraries(afl-main "${CJSON_LIB}")
if (NOT ENABLE_SANITIZERS)
message(FATAL_ERROR "Enable sanitizers with -DENABLE_SANITIZERS=On to do fuzzing.")
endif()
option(ENABLE_FUZZING_PRINT "Fuzz printing functions together with parser." On)
set(fuzz_print_parameter "no")
if (ENABLE_FUZZING_PRINT)
set(fuzz_print_parameter "yes")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error")
add_custom_target(afl
COMMAND "${AFL_FUZZ}" -i "${CMAKE_CURRENT_SOURCE_DIR}/inputs" -o "${CMAKE_CURRENT_BINARY_DIR}/findings" -x "${CMAKE_CURRENT_SOURCE_DIR}/json.dict" -- "${CMAKE_CURRENT_BINARY_DIR}/afl-main" "@@" "${fuzz_print_parameter}"
DEPENDS afl-main)
endif()
if(ENABLE_CJSON_TEST)
ADD_EXECUTABLE(fuzz_main fuzz_main.c cjson_read_fuzzer.c)
TARGET_LINK_LIBRARIES(fuzz_main cjson)
endif()

View File

@ -0,0 +1,5 @@
#!/bin/bash
set -x
echo core | sudo tee /proc/sys/kernel/core_pattern
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

View File

@ -0,0 +1,176 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../cJSON.h"
static char *read_file(const char *filename)
{
FILE *file = NULL;
long length = 0;
char *content = NULL;
size_t read_chars = 0;
/* open in read binary mode */
file = fopen(filename, "rb");
if (file == NULL)
{
goto cleanup;
}
/* get the length */
if (fseek(file, 0, SEEK_END) != 0)
{
goto cleanup;
}
length = ftell(file);
if (length < 0)
{
goto cleanup;
}
if (fseek(file, 0, SEEK_SET) != 0)
{
goto cleanup;
}
/* allocate content buffer */
content = (char*)malloc((size_t)length + sizeof(""));
if (content == NULL)
{
goto cleanup;
}
/* read the file into memory */
read_chars = fread(content, sizeof(char), (size_t)length, file);
if ((long)read_chars != length)
{
free(content);
content = NULL;
goto cleanup;
}
content[read_chars] = '\0';
cleanup:
if (file != NULL)
{
fclose(file);
}
return content;
}
int main(int argc, char** argv)
{
const char *filename = NULL;
cJSON *item = NULL;
char *json = NULL;
int status = EXIT_FAILURE;
char *printed_json = NULL;
if ((argc < 2) || (argc > 3))
{
printf("Usage:\n");
printf("%s input_file [enable_printing]\n", argv[0]);
printf("\t input_file: file containing the test data\n");
printf("\t enable_printing: print after parsing, 'yes' or 'no', defaults to 'no'\n");
goto cleanup;
}
filename = argv[1];
#if __AFL_HAVE_MANUAL_CONTROL
while (__AFL_LOOP(1000))
{
#endif
status = EXIT_SUCCESS;
json = read_file(filename);
if ((json == NULL) || (json[0] == '\0') || (json[1] == '\0'))
{
status = EXIT_FAILURE;
goto cleanup;
}
item = cJSON_Parse(json + 2);
if (item == NULL)
{
goto cleanup;
}
if ((argc == 3) && (strncmp(argv[2], "yes", 3) == 0))
{
int do_format = 0;
if (json[1] == 'f')
{
do_format = 1;
}
if (json[0] == 'b')
{
/* buffered printing */
printed_json = cJSON_PrintBuffered(item, 1, do_format);
}
else
{
/* unbuffered printing */
if (do_format)
{
printed_json = cJSON_Print(item);
}
else
{
printed_json = cJSON_PrintUnformatted(item);
}
}
if (printed_json == NULL)
{
status = EXIT_FAILURE;
goto cleanup;
}
printf("%s\n", printed_json);
}
cleanup:
if (item != NULL)
{
cJSON_Delete(item);
item = NULL;
}
if (json != NULL)
{
free(json);
json = NULL;
}
if (printed_json != NULL)
{
free(printed_json);
printed_json = NULL;
}
#if __AFL_HAVE_MANUAL_CONTROL
}
#endif
return status;
}

View File

@ -0,0 +1,9 @@
#!/bin/bash
mkdir -p afl-build || exit 1
cd afl-build || exit 1
#cleanup
rm -r -- *
CC=afl-clang-fast cmake ../.. -DENABLE_FUZZING=On -DENABLE_SANITIZERS=On -DBUILD_SHARED_LIBS=Off
make afl

View File

@ -0,0 +1,77 @@
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "../cJSON.h"
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); /* required by C89 */
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
cJSON *json;
size_t offset = 4;
unsigned char *copied;
char *printed_json = NULL;
int minify, require_termination, formatted, buffered;
if(size <= offset) return 0;
if(data[size-1] != '\0') return 0;
if(data[0] != '1' && data[0] != '0') return 0;
if(data[1] != '1' && data[1] != '0') return 0;
if(data[2] != '1' && data[2] != '0') return 0;
if(data[3] != '1' && data[3] != '0') return 0;
minify = data[0] == '1' ? 1 : 0;
require_termination = data[1] == '1' ? 1 : 0;
formatted = data[2] == '1' ? 1 : 0;
buffered = data[3] == '1' ? 1 : 0;
json = cJSON_ParseWithOpts((const char*)data + offset, NULL, require_termination);
if(json == NULL) return 0;
if(buffered)
{
printed_json = cJSON_PrintBuffered(json, 1, formatted);
}
else
{
/* unbuffered printing */
if(formatted)
{
printed_json = cJSON_Print(json);
}
else
{
printed_json = cJSON_PrintUnformatted(json);
}
}
if(printed_json != NULL) free(printed_json);
if(minify)
{
copied = (unsigned char*)malloc(size);
if(copied == NULL) return 0;
memcpy(copied, data, size);
cJSON_Minify((char*)copied + offset);
free(copied);
}
cJSON_Delete(json);
return 0;
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,54 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); /* required by C89 */
/* fuzz target entry point, works without libFuzzer */
int main(int argc, char **argv)
{
FILE *f;
char *buf = NULL;
long siz_buf;
if(argc < 2)
{
fprintf(stderr, "no input file\n");
goto err;
}
f = fopen(argv[1], "rb");
if(f == NULL)
{
fprintf(stderr, "error opening input file %s\n", argv[1]);
goto err;
}
fseek(f, 0, SEEK_END);
siz_buf = ftell(f);
rewind(f);
if(siz_buf < 1) goto err;
buf = (char*)malloc((size_t)siz_buf);
if(buf == NULL)
{
fprintf(stderr, "malloc() failed\n");
goto err;
}
if(fread(buf, (size_t)siz_buf, 1, f) != 1)
{
fprintf(stderr, "fread() failed\n");
goto err;
}
(void)LLVMFuzzerTestOneInput((uint8_t*)buf, (size_t)siz_buf);
err:
free(buf);
return 0;
}

View File

@ -0,0 +1,22 @@
bf{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": ["GML", "XML"]
},
"GlossSee": "markup"
}
}
}
}
}

View File

@ -0,0 +1 @@
bf["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]

View File

@ -0,0 +1,8 @@
bf{
"name": "Jack (\"Bee\") Nimble",
"format": {"type": "rect",
"width": 1920,
"height": 1080,
"interlace": false,"frame rate": 24
}
}

View File

@ -0,0 +1,11 @@
bf{"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}}

View File

@ -0,0 +1,26 @@
bf{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}}

View File

@ -0,0 +1,26 @@
bu{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}}

View File

@ -0,0 +1,26 @@
uf{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}}

View File

@ -0,0 +1,26 @@
uu{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}}

View File

@ -0,0 +1,88 @@
bf{"web-app": {
"servlet": [
{
"servlet-name": "cofaxCDS",
"servlet-class": "org.cofax.cds.CDSServlet",
"init-param": {
"configGlossary:installationAt": "Philadelphia, PA",
"configGlossary:adminEmail": "ksm@pobox.com",
"configGlossary:poweredBy": "Cofax",
"configGlossary:poweredByIcon": "/images/cofax.gif",
"configGlossary:staticPath": "/content/static",
"templateProcessorClass": "org.cofax.WysiwygTemplate",
"templateLoaderClass": "org.cofax.FilesTemplateLoader",
"templatePath": "templates",
"templateOverridePath": "",
"defaultListTemplate": "listTemplate.htm",
"defaultFileTemplate": "articleTemplate.htm",
"useJSP": false,
"jspListTemplate": "listTemplate.jsp",
"jspFileTemplate": "articleTemplate.jsp",
"cachePackageTagsTrack": 200,
"cachePackageTagsStore": 200,
"cachePackageTagsRefresh": 60,
"cacheTemplatesTrack": 100,
"cacheTemplatesStore": 50,
"cacheTemplatesRefresh": 15,
"cachePagesTrack": 200,
"cachePagesStore": 100,
"cachePagesRefresh": 10,
"cachePagesDirtyRead": 10,
"searchEngineListTemplate": "forSearchEnginesList.htm",
"searchEngineFileTemplate": "forSearchEngines.htm",
"searchEngineRobotsDb": "WEB-INF/robots.db",
"useDataStore": true,
"dataStoreClass": "org.cofax.SqlDataStore",
"redirectionClass": "org.cofax.SqlRedirection",
"dataStoreName": "cofax",
"dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
"dataStoreUser": "sa",
"dataStorePassword": "dataStoreTestQuery",
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
"dataStoreInitConns": 10,
"dataStoreMaxConns": 100,
"dataStoreConnUsageLimit": 100,
"dataStoreLogLevel": "debug",
"maxUrlLength": 500}},
{
"servlet-name": "cofaxEmail",
"servlet-class": "org.cofax.cds.EmailServlet",
"init-param": {
"mailHost": "mail1",
"mailHostOverride": "mail2"}},
{
"servlet-name": "cofaxAdmin",
"servlet-class": "org.cofax.cds.AdminServlet"},
{
"servlet-name": "fileServlet",
"servlet-class": "org.cofax.cds.FileServlet"},
{
"servlet-name": "cofaxTools",
"servlet-class": "org.cofax.cms.CofaxToolsServlet",
"init-param": {
"templatePath": "toolstemplates/",
"log": 1,
"logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
"logMaxSize": "",
"dataLog": 1,
"dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
"dataLogMaxSize": "",
"removePageCache": "/content/admin/remove?cache=pages&id=",
"removeTemplateCache": "/content/admin/remove?cache=templates&id=",
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
"lookInContext": 1,
"adminGroupID": 4,
"betaServer": true}}],
"servlet-mapping": {
"cofaxCDS": "/",
"cofaxEmail": "/cofaxutil/aemail/*",
"cofaxAdmin": "/admin/*",
"fileServlet": "/static/*",
"cofaxTools": "/tools/*"},
"taglib": {
"taglib-uri": "cofax.tld",
"taglib-location": "/WEB-INF/tlds/cofax.tld"}}}

View File

@ -0,0 +1,27 @@
bf{"menu": {
"header": "SVG Viewer",
"items": [
{"id": "Open"},
{"id": "OpenNew", "label": "Open New"},
null,
{"id": "ZoomIn", "label": "Zoom In"},
{"id": "ZoomOut", "label": "Zoom Out"},
{"id": "OriginalView", "label": "Original View"},
null,
{"id": "Quality"},
{"id": "Pause"},
{"id": "Mute"},
null,
{"id": "Find", "label": "Find..."},
{"id": "FindAgain", "label": "Find Again"},
{"id": "Copy"},
{"id": "CopyAgain", "label": "Copy Again"},
{"id": "CopySVG", "label": "Copy SVG"},
{"id": "ViewSVG", "label": "View SVG"},
{"id": "ViewSource", "label": "View Source"},
{"id": "SaveAs", "label": "Save As"},
null,
{"id": "Help"},
{"id": "About", "label": "About Adobe CVG Viewer..."}
]
}}

View File

@ -0,0 +1,16 @@
bf<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type="text/css">
html, body, iframe { margin: 0; padding: 0; height: 100%; }
iframe { display: block; width: 100%; border: none; }
</style>
<title>Application Error</title>
</head>
<body>
<iframe src="//s3.amazonaws.com/heroku_pages/error.html">
<p>Application Error</p>
</iframe>
</body>
</html>

View File

@ -0,0 +1,22 @@
bf[
{
"precision": "zip",
"Latitude": 37.7668,
"Longitude": -122.3959,
"Address": "",
"City": "SAN FRANCISCO",
"State": "CA",
"Zip": "94107",
"Country": "US"
},
{
"precision": "zip",
"Latitude": 37.371991,
"Longitude": -122.026020,
"Address": "",
"City": "SUNNYVALE",
"State": "CA",
"Zip": "94085",
"Country": "US"
}
]

View File

@ -0,0 +1,13 @@
bf{
"Image": {
"Width": 800,
"Height": 600,
"Title": "View from 15th Floor",
"Thumbnail": {
"Url": "http:/*www.example.com/image/481989943",
"Height": 125,
"Width": "100"
},
"IDs": [116, 943, 234, 38793]
}
}

View File

@ -0,0 +1,5 @@
bf[
[0, -1, 0],
[1, 0, 0],
[0, 0, 1]
]

View File

@ -0,0 +1,47 @@
#
# AFL dictionary for JSON
# -----------------------------
#
object_start="{"
object_end="}"
object_empty="{}"
object_one_element="{\"one\":1}"
object_two_elements="{\"1\":1,\"2\":2}"
object_separator=":"
array_start="["
array_end="]"
array_empty="[]"
array_one_element="[1]"
array_two_elements="[1,2]"
separator=","
escape_sequence_b="\\b"
escape_sequence_f="\\f"
escape_sequence_n="\\n"
escape_sequence_r="\\r"
escape_sequence_t="\\t"
escape_sequence_quote="\\\""
escape_sequence_backslash="\\\\"
escape_sequence_slash="\\/"
escape_sequence_utf16_base="\\u"
escape_sequence_utf16="\\u12ab"
number_integer="1"
number_double="1.0"
number_negative_integer="-1"
number_negative_double="-1.0"
number_engineering1="1e1"
number_engineering2="1e-1"
number_positive_integer="+1"
number_positive_double="+1.0"
number_e="e"
number_plus="+"
number_minus="-"
number_separator="."
null="null"
true="true"
false="false"

View File

@ -0,0 +1,18 @@
#!/bin/bash -eu
# This script is meant to be run by
# https://github.com/google/oss-fuzz/blob/master/projects/cjson/Dockerfile
mkdir build
cd build
cmake -DBUILD_SHARED_LIBS=OFF -DENABLE_CJSON_TEST=OFF ..
make -j$(nproc)
$CXX $CXXFLAGS $SRC/cjson/fuzzing/cjson_read_fuzzer.c -I. \
-o $OUT/cjson_read_fuzzer \
$LIB_FUZZING_ENGINE $SRC/cjson/build/libcjson.a
find $SRC/cjson/fuzzing/inputs -name "*" | \
xargs zip $OUT/cjson_read_fuzzer_seed_corpus.zip
cp $SRC/cjson/fuzzing/json.dict $OUT/cjson_read_fuzzer.dict

View File

@ -0,0 +1,29 @@
# Whether the utils lib was build.
set(CJSON_UTILS_FOUND @ENABLE_CJSON_UTILS@)
# The include directories used by cJSON
set(CJSON_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@")
set(CJSON_INCLUDE_DIR "@CMAKE_INSTALL_FULL_INCLUDEDIR@")
get_filename_component(_dir "${CMAKE_CURRENT_LIST_FILE}" PATH)
# The cJSON library
set(CJSON_LIBRARY "@CJSON_LIB@")
if(@ENABLE_TARGET_EXPORT@)
# Include the target
include("${_dir}/cjson.cmake")
endif()
if(CJSON_UTILS_FOUND)
# The cJSON utils library
set(CJSON_UTILS_LIBRARY @CJSON_UTILS_LIB@)
# All cJSON libraries
set(CJSON_LIBRARIES "@CJSON_UTILS_LIB@" "@CJSON_LIB@")
if(@ENABLE_TARGET_EXPORT@)
# Include the target
include("${_dir}/cjson_utils.cmake")
endif()
else()
# All cJSON libraries
set(CJSON_LIBRARIES "@CJSON_LIB@")
endif()

View File

@ -0,0 +1,11 @@
set(PACKAGE_VERSION "@PROJECT_VERSION@")
# Check whether the requested PACKAGE_FIND_VERSION is compatible
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()

View File

@ -0,0 +1,10 @@
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
Name: libcjson
Version: @PROJECT_VERSION@
Description: Ultralightweight JSON parser in ANSI C
URL: https://github.com/DaveGamble/cJSON
Libs: -L${libdir} -lcjson
Libs.private: -lm
Cflags: -I${includedir} -I${includedir}/cjson

View File

@ -0,0 +1,10 @@
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
Name: libcjson_utils
Version: @PROJECT_VERSION@
Description: An implementation of JSON Pointer, Patch and Merge Patch based on cJSON.
URL: https://github.com/DaveGamble/cJSON
Libs: -L${libdir} -lcjson_utils
Cflags: -I${includedir} -I${includedir}/cjson
Requires: libcjson

View File

@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 2.8.5)
set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt")
if(NOT EXISTS ${MANIFEST})
message(FATAL_ERROR "Cannot find install mainfest: ${MANIFEST}")
endif()
file(STRINGS ${MANIFEST} files)
foreach(file ${files})
if(EXISTS ${file} OR IS_SYMLINK ${file})
message(STATUS "Removing: ${file}")
execute_process(COMMAND rm -f ${file}
RESULT_VARIABLE result
OUTPUT_QUIET
ERROR_VARIABLE stderr
ERROR_STRIP_TRAILING_WHITESPACE
)
if(NOT ${result} EQUAL 0)
message(FATAL_ERROR "${stderr}")
endif()
else()
message(STATUS "Does-not-exist: ${file}")
endif()
endforeach(file)

268
components/cjson/test.c Normal file
View File

@ -0,0 +1,268 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"
/* Used by some code below as an example datatype. */
struct record
{
const char *precision;
double lat;
double lon;
const char *address;
const char *city;
const char *state;
const char *zip;
const char *country;
};
/* Create a bunch of objects as demonstration. */
static int print_preallocated(cJSON *root)
{
/* declarations */
char *out = NULL;
char *buf = NULL;
char *buf_fail = NULL;
size_t len = 0;
size_t len_fail = 0;
/* formatted print */
out = cJSON_Print(root);
/* create buffer to succeed */
/* the extra 5 bytes are because of inaccuracies when reserving memory */
len = strlen(out) + 5;
buf = (char*)malloc(len);
if (buf == NULL)
{
printf("Failed to allocate memory.\n");
exit(1);
}
/* create buffer to fail */
len_fail = strlen(out);
buf_fail = (char*)malloc(len_fail);
if (buf_fail == NULL)
{
printf("Failed to allocate memory.\n");
exit(1);
}
/* Print to buffer */
if (!cJSON_PrintPreallocated(root, buf, (int)len, 1)) {
printf("cJSON_PrintPreallocated failed!\n");
if (strcmp(out, buf) != 0) {
printf("cJSON_PrintPreallocated not the same as cJSON_Print!\n");
printf("cJSON_Print result:\n%s\n", out);
printf("cJSON_PrintPreallocated result:\n%s\n", buf);
}
free(out);
free(buf_fail);
free(buf);
return -1;
}
/* success */
printf("%s\n", buf);
/* force it to fail */
if (cJSON_PrintPreallocated(root, buf_fail, (int)len_fail, 1)) {
printf("cJSON_PrintPreallocated failed to show error with insufficient memory!\n");
printf("cJSON_Print result:\n%s\n", out);
printf("cJSON_PrintPreallocated result:\n%s\n", buf_fail);
free(out);
free(buf_fail);
free(buf);
return -1;
}
free(out);
free(buf_fail);
free(buf);
return 0;
}
/* Create a bunch of objects as demonstration. */
static void create_objects(void)
{
/* declare a few. */
cJSON *root = NULL;
cJSON *fmt = NULL;
cJSON *img = NULL;
cJSON *thm = NULL;
cJSON *fld = NULL;
int i = 0;
/* Our "days of the week" array: */
const char *strings[7] =
{
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
};
/* Our matrix: */
int numbers[3][3] =
{
{0, -1, 0},
{1, 0, 0},
{0 ,0, 1}
};
/* Our "gallery" item: */
int ids[4] = { 116, 943, 234, 38793 };
/* Our array of "records": */
struct record fields[2] =
{
{
"zip",
37.7668,
-1.223959e+2,
"",
"SAN FRANCISCO",
"CA",
"94107",
"US"
},
{
"zip",
37.371991,
-1.22026e+2,
"",
"SUNNYVALE",
"CA",
"94085",
"US"
}
};
volatile double zero = 0.0;
/* Here we construct some JSON standards, from the JSON site. */
/* Our "Video" datatype: */
root = cJSON_CreateObject();
cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
cJSON_AddItemToObject(root, "format", fmt = cJSON_CreateObject());
cJSON_AddStringToObject(fmt, "type", "rect");
cJSON_AddNumberToObject(fmt, "width", 1920);
cJSON_AddNumberToObject(fmt, "height", 1080);
cJSON_AddFalseToObject (fmt, "interlace");
cJSON_AddNumberToObject(fmt, "frame rate", 24);
/* Print to text */
if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
/* Our "days of the week" array: */
root = cJSON_CreateStringArray(strings, 7);
if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
/* Our matrix: */
root = cJSON_CreateArray();
for (i = 0; i < 3; i++)
{
cJSON_AddItemToArray(root, cJSON_CreateIntArray(numbers[i], 3));
}
/* cJSON_ReplaceItemInArray(root, 1, cJSON_CreateString("Replacement")); */
if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
/* Our "gallery" item: */
root = cJSON_CreateObject();
cJSON_AddItemToObject(root, "Image", img = cJSON_CreateObject());
cJSON_AddNumberToObject(img, "Width", 800);
cJSON_AddNumberToObject(img, "Height", 600);
cJSON_AddStringToObject(img, "Title", "View from 15th Floor");
cJSON_AddItemToObject(img, "Thumbnail", thm = cJSON_CreateObject());
cJSON_AddStringToObject(thm, "Url", "http:/*www.example.com/image/481989943");
cJSON_AddNumberToObject(thm, "Height", 125);
cJSON_AddStringToObject(thm, "Width", "100");
cJSON_AddItemToObject(img, "IDs", cJSON_CreateIntArray(ids, 4));
if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
/* Our array of "records": */
root = cJSON_CreateArray();
for (i = 0; i < 2; i++)
{
cJSON_AddItemToArray(root, fld = cJSON_CreateObject());
cJSON_AddStringToObject(fld, "precision", fields[i].precision);
cJSON_AddNumberToObject(fld, "Latitude", fields[i].lat);
cJSON_AddNumberToObject(fld, "Longitude", fields[i].lon);
cJSON_AddStringToObject(fld, "Address", fields[i].address);
cJSON_AddStringToObject(fld, "City", fields[i].city);
cJSON_AddStringToObject(fld, "State", fields[i].state);
cJSON_AddStringToObject(fld, "Zip", fields[i].zip);
cJSON_AddStringToObject(fld, "Country", fields[i].country);
}
/* cJSON_ReplaceItemInObject(cJSON_GetArrayItem(root, 1), "City", cJSON_CreateIntArray(ids, 4)); */
if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "number", 1.0 / zero);
if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
}
int CJSON_CDECL main(void)
{
/* print the version */
printf("Version: %s\n", cJSON_Version());
/* Now some samplecode for building objects concisely: */
create_objects();
return 0;
}

View File

@ -0,0 +1,119 @@
if(ENABLE_CJSON_TEST)
add_library(unity STATIC unity/src/unity.c)
# Disable -Werror for Unity
if (FLAG_SUPPORTED_Werror)
if ("${CMAKE_VERSION}" VERSION_LESS "2.8.12")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error")
else()
target_compile_options(unity PRIVATE "-Wno-error")
endif()
endif()
# Disable -fvisibility=hidden for Unity
if (FLAG_SUPPORTED_fvisibilityhidden)
if ("${CMAKE_VERSION}" VERSION_LESS "2.8.12")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=default")
else()
target_compile_options(unity PRIVATE "-fvisibility=default")
endif()
endif()
# Disable -fsanitize=float-divide-by-zero for Unity (GCC bug on x86 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80097)
if (FLAG_SUPPORTED_fsanitizefloatdividebyzero AND (CMAKE_C_COMPILER_ID STREQUAL "GNU"))
if ("${CMAKE_VERSION}" VERSION_LESS "2.8.12")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-sanitize=float-divide-by-zero")
else()
target_compile_options(unity PRIVATE "-fno-sanitize=float-divide-by-zero")
endif()
endif()
# Disable -Wswitch-enum for Unity
if (FLAG_SUPPORTED_Wswitchenum)
if ("${CMAKE_VERSION}" VERSION_LESS "2.8.12")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-switch-enum")
else()
target_compile_options(unity PRIVATE "-Wno-switch-enum")
endif()
endif()
#copy test files
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/inputs")
file(GLOB test_files "inputs/*")
file(COPY ${test_files} DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/inputs/")
set(unity_tests
parse_examples
parse_number
parse_hex4
parse_string
parse_array
parse_object
parse_value
print_string
print_number
print_array
print_object
print_value
misc_tests
parse_with_opts
compare_tests
cjson_add
readme_examples
minify_tests
)
option(ENABLE_VALGRIND OFF "Enable the valgrind memory checker for the tests.")
if (ENABLE_VALGRIND)
find_program(MEMORYCHECK_COMMAND valgrind)
if ("${MEMORYCHECK_COMMAND}" MATCHES "MEMORYCHECK_COMMAND-NOTFOUND")
message(WARNING "Valgrind couldn't be found.")
unset(MEMORYCHECK_COMMAND)
else()
set(MEMORYCHECK_COMMAND_OPTIONS --trace-children=yes --leak-check=full --error-exitcode=1 --suppressions=${CMAKE_CURRENT_SOURCE_DIR}/../valgrind.supp)
endif()
endif()
foreach(unity_test ${unity_tests})
add_executable("${unity_test}" "${unity_test}.c")
if("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
target_sources(${unity_test} PRIVATE unity_setup.c)
endif()
target_link_libraries("${unity_test}" "${CJSON_LIB}" unity)
if(MEMORYCHECK_COMMAND)
add_test(NAME "${unity_test}"
COMMAND "${MEMORYCHECK_COMMAND}" ${MEMORYCHECK_COMMAND_OPTIONS} "${CMAKE_CURRENT_BINARY_DIR}/${unity_test}")
else()
add_test(NAME "${unity_test}"
COMMAND "./${unity_test}")
endif()
endforeach()
add_dependencies(check ${unity_tests})
if (ENABLE_CJSON_UTILS)
#copy test files
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/json-patch-tests")
file(GLOB test_files "json-patch-tests/*")
file(COPY ${test_files} DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/json-patch-tests/")
set (cjson_utils_tests
json_patch_tests
old_utils_tests
misc_utils_tests)
foreach (cjson_utils_test ${cjson_utils_tests})
add_executable("${cjson_utils_test}" "${cjson_utils_test}.c")
target_link_libraries("${cjson_utils_test}" "${CJSON_LIB}" "${CJSON_UTILS_LIB}" unity)
if("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
target_sources(${cjson_utils_test} PRIVATE unity_setup.c)
endif()
if(MEMORYCHECK_COMMAND)
add_test(NAME "${cjson_utils_test}"
COMMAND "${MEMORYCHECK_COMMAND}" ${MEMORYCHECK_COMMAND_OPTIONS} "${CMAKE_CURRENT_BINARY_DIR}/${cjson_utils_test}")
else()
add_test(NAME "${cjson_utils_test}"
COMMAND "./${cjson_utils_test}")
endif()
endforeach()
add_dependencies(check ${cjson_utils_tests})
endif()
endif()

View File

@ -0,0 +1,471 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "unity/examples/unity_config.h"
#include "unity/src/unity.h"
#include "common.h"
static void * CJSON_CDECL failing_malloc(size_t size)
{
(void)size;
return NULL;
}
/* work around MSVC error C2322: '...' address of dillimport '...' is not static */
static void CJSON_CDECL normal_free(void *pointer)
{
free(pointer);
}
static cJSON_Hooks failing_hooks = {
failing_malloc,
normal_free
};
static void cjson_add_null_should_add_null(void)
{
cJSON *root = cJSON_CreateObject();
cJSON *null = NULL;
cJSON_AddNullToObject(root, "null");
TEST_ASSERT_NOT_NULL(null = cJSON_GetObjectItemCaseSensitive(root, "null"));
TEST_ASSERT_EQUAL_INT(null->type, cJSON_NULL);
cJSON_Delete(root);
}
static void cjson_add_null_should_fail_with_null_pointers(void)
{
cJSON *root = cJSON_CreateObject();
TEST_ASSERT_NULL(cJSON_AddNullToObject(NULL, "null"));
TEST_ASSERT_NULL(cJSON_AddNullToObject(root, NULL));
cJSON_Delete(root);
}
static void cjson_add_null_should_fail_on_allocation_failure(void)
{
cJSON *root = cJSON_CreateObject();
cJSON_InitHooks(&failing_hooks);
TEST_ASSERT_NULL(cJSON_AddNullToObject(root, "null"));
cJSON_InitHooks(NULL);
cJSON_Delete(root);
}
static void cjson_add_true_should_add_true(void)
{
cJSON *root = cJSON_CreateObject();
cJSON *true_item = NULL;
cJSON_AddTrueToObject(root, "true");
TEST_ASSERT_NOT_NULL(true_item = cJSON_GetObjectItemCaseSensitive(root, "true"));
TEST_ASSERT_EQUAL_INT(true_item->type, cJSON_True);
cJSON_Delete(root);
}
static void cjson_add_true_should_fail_with_null_pointers(void)
{
cJSON *root = cJSON_CreateObject();
TEST_ASSERT_NULL(cJSON_AddTrueToObject(NULL, "true"));
TEST_ASSERT_NULL(cJSON_AddTrueToObject(root, NULL));
cJSON_Delete(root);
}
static void cjson_add_true_should_fail_on_allocation_failure(void)
{
cJSON *root = cJSON_CreateObject();
cJSON_InitHooks(&failing_hooks);
TEST_ASSERT_NULL(cJSON_AddTrueToObject(root, "true"));
cJSON_InitHooks(NULL);
cJSON_Delete(root);
}
static void cjson_create_int_array_should_fail_on_allocation_failure(void)
{
int numbers[] = {1, 2, 3};
cJSON_InitHooks(&failing_hooks);
TEST_ASSERT_NULL(cJSON_CreateIntArray(numbers, 3));
cJSON_InitHooks(NULL);
}
static void cjson_create_float_array_should_fail_on_allocation_failure(void)
{
float numbers[] = {1.0f, 2.0f, 3.0f};
cJSON_InitHooks(&failing_hooks);
TEST_ASSERT_NULL(cJSON_CreateFloatArray(numbers, 3));
cJSON_InitHooks(NULL);
}
static void cjson_create_double_array_should_fail_on_allocation_failure(void)
{
double numbers[] = {1.0, 2.0, 3.0};
cJSON_InitHooks(&failing_hooks);
TEST_ASSERT_NULL(cJSON_CreateDoubleArray(numbers, 3));
cJSON_InitHooks(NULL);
}
static void cjson_create_string_array_should_fail_on_allocation_failure(void)
{
const char* strings[] = {"1", "2", "3"};
cJSON_InitHooks(&failing_hooks);
TEST_ASSERT_NULL(cJSON_CreateStringArray(strings, 3));
cJSON_InitHooks(NULL);
}
static void cjson_add_false_should_add_false(void)
{
cJSON *root = cJSON_CreateObject();
cJSON *false_item = NULL;
cJSON_AddFalseToObject(root, "false");
TEST_ASSERT_NOT_NULL(false_item = cJSON_GetObjectItemCaseSensitive(root, "false"));
TEST_ASSERT_EQUAL_INT(false_item->type, cJSON_False);
cJSON_Delete(root);
}
static void cjson_add_false_should_fail_with_null_pointers(void)
{
cJSON *root = cJSON_CreateObject();
TEST_ASSERT_NULL(cJSON_AddFalseToObject(NULL, "false"));
TEST_ASSERT_NULL(cJSON_AddFalseToObject(root, NULL));
cJSON_Delete(root);
}
static void cjson_add_false_should_fail_on_allocation_failure(void)
{
cJSON *root = cJSON_CreateObject();
cJSON_InitHooks(&failing_hooks);
TEST_ASSERT_NULL(cJSON_AddFalseToObject(root, "false"));
cJSON_InitHooks(NULL);
cJSON_Delete(root);
}
static void cjson_add_bool_should_add_bool(void)
{
cJSON *root = cJSON_CreateObject();
cJSON *true_item = NULL;
cJSON *false_item = NULL;
/* true */
cJSON_AddBoolToObject(root, "true", true);
TEST_ASSERT_NOT_NULL(true_item = cJSON_GetObjectItemCaseSensitive(root, "true"));
TEST_ASSERT_EQUAL_INT(true_item->type, cJSON_True);
/* false */
cJSON_AddBoolToObject(root, "false", false);
TEST_ASSERT_NOT_NULL(false_item = cJSON_GetObjectItemCaseSensitive(root, "false"));
TEST_ASSERT_EQUAL_INT(false_item->type, cJSON_False);
cJSON_Delete(root);
}
static void cjson_add_bool_should_fail_with_null_pointers(void)
{
cJSON *root = cJSON_CreateObject();
TEST_ASSERT_NULL(cJSON_AddBoolToObject(NULL, "false", false));
TEST_ASSERT_NULL(cJSON_AddBoolToObject(root, NULL, false));
cJSON_Delete(root);
}
static void cjson_add_bool_should_fail_on_allocation_failure(void)
{
cJSON *root = cJSON_CreateObject();
cJSON_InitHooks(&failing_hooks);
TEST_ASSERT_NULL(cJSON_AddBoolToObject(root, "false", false));
cJSON_InitHooks(NULL);
cJSON_Delete(root);
}
static void cjson_add_number_should_add_number(void)
{
cJSON *root = cJSON_CreateObject();
cJSON *number = NULL;
cJSON_AddNumberToObject(root, "number", 42);
TEST_ASSERT_NOT_NULL(number = cJSON_GetObjectItemCaseSensitive(root, "number"));
TEST_ASSERT_EQUAL_INT(number->type, cJSON_Number);
TEST_ASSERT_EQUAL_DOUBLE(number->valuedouble, 42);
TEST_ASSERT_EQUAL_INT(number->valueint, 42);
cJSON_Delete(root);
}
static void cjson_add_number_should_fail_with_null_pointers(void)
{
cJSON *root = cJSON_CreateObject();
TEST_ASSERT_NULL(cJSON_AddNumberToObject(NULL, "number", 42));
TEST_ASSERT_NULL(cJSON_AddNumberToObject(root, NULL, 42));
cJSON_Delete(root);
}
static void cjson_add_number_should_fail_on_allocation_failure(void)
{
cJSON *root = cJSON_CreateObject();
cJSON_InitHooks(&failing_hooks);
TEST_ASSERT_NULL(cJSON_AddNumberToObject(root, "number", 42));
cJSON_InitHooks(NULL);
cJSON_Delete(root);
}
static void cjson_add_string_should_add_string(void)
{
cJSON *root = cJSON_CreateObject();
cJSON *string = NULL;
cJSON_AddStringToObject(root, "string", "Hello World!");
TEST_ASSERT_NOT_NULL(string = cJSON_GetObjectItemCaseSensitive(root, "string"));
TEST_ASSERT_EQUAL_INT(string->type, cJSON_String);
TEST_ASSERT_EQUAL_STRING(string->valuestring, "Hello World!");
cJSON_Delete(root);
}
static void cjson_add_string_should_fail_with_null_pointers(void)
{
cJSON *root = cJSON_CreateObject();
TEST_ASSERT_NULL(cJSON_AddStringToObject(NULL, "string", "string"));
TEST_ASSERT_NULL(cJSON_AddStringToObject(root, NULL, "string"));
cJSON_Delete(root);
}
static void cjson_add_string_should_fail_on_allocation_failure(void)
{
cJSON *root = cJSON_CreateObject();
cJSON_InitHooks(&failing_hooks);
TEST_ASSERT_NULL(cJSON_AddStringToObject(root, "string", "string"));
cJSON_InitHooks(NULL);
cJSON_Delete(root);
}
static void cjson_add_raw_should_add_raw(void)
{
cJSON *root = cJSON_CreateObject();
cJSON *raw = NULL;
cJSON_AddRawToObject(root, "raw", "{}");
TEST_ASSERT_NOT_NULL(raw = cJSON_GetObjectItemCaseSensitive(root, "raw"));
TEST_ASSERT_EQUAL_INT(raw->type, cJSON_Raw);
TEST_ASSERT_EQUAL_STRING(raw->valuestring, "{}");
cJSON_Delete(root);
}
static void cjson_add_raw_should_fail_with_null_pointers(void)
{
cJSON *root = cJSON_CreateObject();
TEST_ASSERT_NULL(cJSON_AddRawToObject(NULL, "raw", "{}"));
TEST_ASSERT_NULL(cJSON_AddRawToObject(root, NULL, "{}"));
cJSON_Delete(root);
}
static void cjson_add_raw_should_fail_on_allocation_failure(void)
{
cJSON *root = cJSON_CreateObject();
cJSON_InitHooks(&failing_hooks);
TEST_ASSERT_NULL(cJSON_AddRawToObject(root, "raw", "{}"));
cJSON_InitHooks(NULL);
cJSON_Delete(root);
}
static void cJSON_add_object_should_add_object(void)
{
cJSON *root = cJSON_CreateObject();
cJSON *object = NULL;
cJSON_AddObjectToObject(root, "object");
TEST_ASSERT_NOT_NULL(object = cJSON_GetObjectItemCaseSensitive(root, "object"));
TEST_ASSERT_EQUAL_INT(object->type, cJSON_Object);
cJSON_Delete(root);
}
static void cjson_add_object_should_fail_with_null_pointers(void)
{
cJSON *root = cJSON_CreateObject();
TEST_ASSERT_NULL(cJSON_AddObjectToObject(NULL, "object"));
TEST_ASSERT_NULL(cJSON_AddObjectToObject(root, NULL));
cJSON_Delete(root);
}
static void cjson_add_object_should_fail_on_allocation_failure(void)
{
cJSON *root = cJSON_CreateObject();
cJSON_InitHooks(&failing_hooks);
TEST_ASSERT_NULL(cJSON_AddObjectToObject(root, "object"));
cJSON_InitHooks(NULL);
cJSON_Delete(root);
}
static void cJSON_add_array_should_add_array(void)
{
cJSON *root = cJSON_CreateObject();
cJSON *array = NULL;
cJSON_AddArrayToObject(root, "array");
TEST_ASSERT_NOT_NULL(array = cJSON_GetObjectItemCaseSensitive(root, "array"));
TEST_ASSERT_EQUAL_INT(array->type, cJSON_Array);
cJSON_Delete(root);
}
static void cjson_add_array_should_fail_with_null_pointers(void)
{
cJSON *root = cJSON_CreateObject();
TEST_ASSERT_NULL(cJSON_AddArrayToObject(NULL, "array"));
TEST_ASSERT_NULL(cJSON_AddArrayToObject(root, NULL));
cJSON_Delete(root);
}
static void cjson_add_array_should_fail_on_allocation_failure(void)
{
cJSON *root = cJSON_CreateObject();
cJSON_InitHooks(&failing_hooks);
TEST_ASSERT_NULL(cJSON_AddArrayToObject(root, "array"));
cJSON_InitHooks(NULL);
cJSON_Delete(root);
}
int CJSON_CDECL main(void)
{
UNITY_BEGIN();
RUN_TEST(cjson_add_null_should_add_null);
RUN_TEST(cjson_add_null_should_fail_with_null_pointers);
RUN_TEST(cjson_add_null_should_fail_on_allocation_failure);
RUN_TEST(cjson_add_true_should_add_true);
RUN_TEST(cjson_add_true_should_fail_with_null_pointers);
RUN_TEST(cjson_add_true_should_fail_on_allocation_failure);
RUN_TEST(cjson_create_int_array_should_fail_on_allocation_failure);
RUN_TEST(cjson_create_float_array_should_fail_on_allocation_failure);
RUN_TEST(cjson_create_double_array_should_fail_on_allocation_failure);
RUN_TEST(cjson_create_string_array_should_fail_on_allocation_failure);
RUN_TEST(cjson_add_false_should_add_false);
RUN_TEST(cjson_add_false_should_fail_with_null_pointers);
RUN_TEST(cjson_add_false_should_fail_on_allocation_failure);
RUN_TEST(cjson_add_bool_should_add_bool);
RUN_TEST(cjson_add_bool_should_fail_with_null_pointers);
RUN_TEST(cjson_add_bool_should_fail_on_allocation_failure);
RUN_TEST(cjson_add_number_should_add_number);
RUN_TEST(cjson_add_number_should_fail_with_null_pointers);
RUN_TEST(cjson_add_number_should_fail_on_allocation_failure);
RUN_TEST(cjson_add_string_should_add_string);
RUN_TEST(cjson_add_string_should_fail_with_null_pointers);
RUN_TEST(cjson_add_string_should_fail_on_allocation_failure);
RUN_TEST(cjson_add_raw_should_add_raw);
RUN_TEST(cjson_add_raw_should_fail_with_null_pointers);
RUN_TEST(cjson_add_raw_should_fail_on_allocation_failure);
RUN_TEST(cJSON_add_object_should_add_object);
RUN_TEST(cjson_add_object_should_fail_with_null_pointers);
RUN_TEST(cjson_add_object_should_fail_on_allocation_failure);
RUN_TEST(cJSON_add_array_should_add_array);
RUN_TEST(cjson_add_array_should_fail_with_null_pointers);
RUN_TEST(cjson_add_array_should_fail_on_allocation_failure);
return UNITY_END();
}

View File

@ -0,0 +1,122 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef CJSON_TESTS_COMMON_H
#define CJSON_TESTS_COMMON_H
#include "../cJSON.c"
void reset(cJSON *item);
void reset(cJSON *item) {
if ((item != NULL) && (item->child != NULL))
{
cJSON_Delete(item->child);
}
if ((item->valuestring != NULL) && !(item->type & cJSON_IsReference))
{
global_hooks.deallocate(item->valuestring);
}
if ((item->string != NULL) && !(item->type & cJSON_StringIsConst))
{
global_hooks.deallocate(item->string);
}
memset(item, 0, sizeof(cJSON));
}
char* read_file(const char *filename);
char* read_file(const char *filename) {
FILE *file = NULL;
long length = 0;
char *content = NULL;
size_t read_chars = 0;
/* open in read binary mode */
file = fopen(filename, "rb");
if (file == NULL)
{
goto cleanup;
}
/* get the length */
if (fseek(file, 0, SEEK_END) != 0)
{
goto cleanup;
}
length = ftell(file);
if (length < 0)
{
goto cleanup;
}
if (fseek(file, 0, SEEK_SET) != 0)
{
goto cleanup;
}
/* allocate content buffer */
content = (char*)malloc((size_t)length + sizeof(""));
if (content == NULL)
{
goto cleanup;
}
/* read the file into memory */
read_chars = fread(content, sizeof(char), (size_t)length, file);
if ((long)read_chars != length)
{
free(content);
content = NULL;
goto cleanup;
}
content[read_chars] = '\0';
cleanup:
if (file != NULL)
{
fclose(file);
}
return content;
}
/* assertion helper macros */
#define assert_has_type(item, item_type) TEST_ASSERT_BITS_MESSAGE(0xFF, item_type, item->type, "Item doesn't have expected type.")
#define assert_has_no_reference(item) TEST_ASSERT_BITS_MESSAGE(cJSON_IsReference, 0, item->type, "Item should not have a string as reference.")
#define assert_has_no_const_string(item) TEST_ASSERT_BITS_MESSAGE(cJSON_StringIsConst, 0, item->type, "Item should not have a const string.")
#define assert_has_valuestring(item) TEST_ASSERT_NOT_NULL_MESSAGE(item->valuestring, "Valuestring is NULL.")
#define assert_has_no_valuestring(item) TEST_ASSERT_NULL_MESSAGE(item->valuestring, "Valuestring is not NULL.")
#define assert_has_string(item) TEST_ASSERT_NOT_NULL_MESSAGE(item->string, "String is NULL")
#define assert_has_no_string(item) TEST_ASSERT_NULL_MESSAGE(item->string, "String is not NULL.")
#define assert_not_in_list(item) \
TEST_ASSERT_NULL_MESSAGE(item->next, "Linked list next pointer is not NULL.");\
TEST_ASSERT_NULL_MESSAGE(item->prev, "Linked list previous pointer is not NULL.")
#define assert_has_child(item) TEST_ASSERT_NOT_NULL_MESSAGE(item->child, "Item doesn't have a child.")
#define assert_has_no_child(item) TEST_ASSERT_NULL_MESSAGE(item->child, "Item has a child.")
#define assert_is_invalid(item) \
assert_has_type(item, cJSON_Invalid);\
assert_not_in_list(item);\
assert_has_no_child(item);\
assert_has_no_string(item);\
assert_has_no_valuestring(item)
#endif

View File

@ -0,0 +1,208 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "unity/examples/unity_config.h"
#include "unity/src/unity.h"
#include "common.h"
static cJSON_bool compare_from_string(const char * const a, const char * const b, const cJSON_bool case_sensitive)
{
cJSON *a_json = NULL;
cJSON *b_json = NULL;
cJSON_bool result = false;
a_json = cJSON_Parse(a);
TEST_ASSERT_NOT_NULL_MESSAGE(a_json, "Failed to parse a.");
b_json = cJSON_Parse(b);
TEST_ASSERT_NOT_NULL_MESSAGE(b_json, "Failed to parse b.");
result = cJSON_Compare(a_json, b_json, case_sensitive);
cJSON_Delete(a_json);
cJSON_Delete(b_json);
return result;
}
static void cjson_compare_should_compare_null_pointer_as_not_equal(void)
{
TEST_ASSERT_FALSE(cJSON_Compare(NULL, NULL, true));
TEST_ASSERT_FALSE(cJSON_Compare(NULL, NULL, false));
}
static void cjson_compare_should_compare_invalid_as_not_equal(void)
{
cJSON invalid[1];
memset(invalid, '\0', sizeof(invalid));
TEST_ASSERT_FALSE(cJSON_Compare(invalid, invalid, false));
TEST_ASSERT_FALSE(cJSON_Compare(invalid, invalid, true));
}
static void cjson_compare_should_compare_numbers(void)
{
TEST_ASSERT_TRUE(compare_from_string("1", "1", true));
TEST_ASSERT_TRUE(compare_from_string("1", "1", false));
TEST_ASSERT_TRUE(compare_from_string("0.0001", "0.0001", true));
TEST_ASSERT_TRUE(compare_from_string("0.0001", "0.0001", false));
TEST_ASSERT_TRUE(compare_from_string("1E100", "10E99", false));
TEST_ASSERT_FALSE(compare_from_string("0.5E-100", "0.5E-101", false));
TEST_ASSERT_FALSE(compare_from_string("1", "2", true));
TEST_ASSERT_FALSE(compare_from_string("1", "2", false));
}
static void cjson_compare_should_compare_booleans(void)
{
/* true */
TEST_ASSERT_TRUE(compare_from_string("true", "true", true));
TEST_ASSERT_TRUE(compare_from_string("true", "true", false));
/* false */
TEST_ASSERT_TRUE(compare_from_string("false", "false", true));
TEST_ASSERT_TRUE(compare_from_string("false", "false", false));
/* mixed */
TEST_ASSERT_FALSE(compare_from_string("true", "false", true));
TEST_ASSERT_FALSE(compare_from_string("true", "false", false));
TEST_ASSERT_FALSE(compare_from_string("false", "true", true));
TEST_ASSERT_FALSE(compare_from_string("false", "true", false));
}
static void cjson_compare_should_compare_null(void)
{
TEST_ASSERT_TRUE(compare_from_string("null", "null", true));
TEST_ASSERT_TRUE(compare_from_string("null", "null", false));
TEST_ASSERT_FALSE(compare_from_string("null", "true", true));
TEST_ASSERT_FALSE(compare_from_string("null", "true", false));
}
static void cjson_compare_should_not_accept_invalid_types(void)
{
cJSON invalid[1];
memset(invalid, '\0', sizeof(invalid));
invalid->type = cJSON_Number | cJSON_String;
TEST_ASSERT_FALSE(cJSON_Compare(invalid, invalid, true));
TEST_ASSERT_FALSE(cJSON_Compare(invalid, invalid, false));
}
static void cjson_compare_should_compare_strings(void)
{
TEST_ASSERT_TRUE(compare_from_string("\"abcdefg\"", "\"abcdefg\"", true));
TEST_ASSERT_TRUE(compare_from_string("\"abcdefg\"", "\"abcdefg\"", false));
TEST_ASSERT_FALSE(compare_from_string("\"ABCDEFG\"", "\"abcdefg\"", true));
TEST_ASSERT_FALSE(compare_from_string("\"ABCDEFG\"", "\"abcdefg\"", false));
}
static void cjson_compare_should_compare_raw(void)
{
cJSON *raw1 = NULL;
cJSON *raw2 = NULL;
raw1 = cJSON_Parse("\"[true, false]\"");
TEST_ASSERT_NOT_NULL(raw1);
raw2 = cJSON_Parse("\"[true, false]\"");
TEST_ASSERT_NOT_NULL(raw2);
raw1->type = cJSON_Raw;
raw2->type = cJSON_Raw;
TEST_ASSERT_TRUE(cJSON_Compare(raw1, raw2, true));
TEST_ASSERT_TRUE(cJSON_Compare(raw1, raw2, false));
cJSON_Delete(raw1);
cJSON_Delete(raw2);
}
static void cjson_compare_should_compare_arrays(void)
{
TEST_ASSERT_TRUE(compare_from_string("[]", "[]", true));
TEST_ASSERT_TRUE(compare_from_string("[]", "[]", false));
TEST_ASSERT_TRUE(compare_from_string("[false,true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", true));
TEST_ASSERT_TRUE(compare_from_string("[false,true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", false));
TEST_ASSERT_TRUE(compare_from_string("[[[1], 2]]", "[[[1], 2]]", true));
TEST_ASSERT_TRUE(compare_from_string("[[[1], 2]]", "[[[1], 2]]", false));
TEST_ASSERT_FALSE(compare_from_string("[true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", true));
TEST_ASSERT_FALSE(compare_from_string("[true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", false));
/* Arrays that are a prefix of another array */
TEST_ASSERT_FALSE(compare_from_string("[1,2,3]", "[1,2]", true));
TEST_ASSERT_FALSE(compare_from_string("[1,2,3]", "[1,2]", false));
}
static void cjson_compare_should_compare_objects(void)
{
TEST_ASSERT_TRUE(compare_from_string("{}", "{}", true));
TEST_ASSERT_TRUE(compare_from_string("{}", "{}", false));
TEST_ASSERT_TRUE(compare_from_string(
"{\"false\": false, \"true\": true, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
"{\"true\": true, \"false\": false, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
true));
TEST_ASSERT_FALSE(compare_from_string(
"{\"False\": false, \"true\": true, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
"{\"true\": true, \"false\": false, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
true));
TEST_ASSERT_TRUE(compare_from_string(
"{\"False\": false, \"true\": true, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
"{\"true\": true, \"false\": false, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
false));
TEST_ASSERT_FALSE(compare_from_string(
"{\"Flse\": false, \"true\": true, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
"{\"true\": true, \"false\": false, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
false));
/* test objects that are a subset of each other */
TEST_ASSERT_FALSE(compare_from_string(
"{\"one\": 1, \"two\": 2}",
"{\"one\": 1, \"two\": 2, \"three\": 3}",
true))
TEST_ASSERT_FALSE(compare_from_string(
"{\"one\": 1, \"two\": 2}",
"{\"one\": 1, \"two\": 2, \"three\": 3}",
false))
}
int CJSON_CDECL main(void)
{
UNITY_BEGIN();
RUN_TEST(cjson_compare_should_compare_null_pointer_as_not_equal);
RUN_TEST(cjson_compare_should_compare_invalid_as_not_equal);
RUN_TEST(cjson_compare_should_compare_numbers);
RUN_TEST(cjson_compare_should_compare_booleans);
RUN_TEST(cjson_compare_should_compare_null);
RUN_TEST(cjson_compare_should_not_accept_invalid_types);
RUN_TEST(cjson_compare_should_compare_strings);
RUN_TEST(cjson_compare_should_compare_raw);
RUN_TEST(cjson_compare_should_compare_arrays);
RUN_TEST(cjson_compare_should_compare_objects);
return UNITY_END();
}

View File

@ -0,0 +1,22 @@
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": ["GML", "XML"]
},
"GlossSee": "markup"
}
}
}
}
}

View File

@ -0,0 +1,22 @@
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": ["GML", "XML"]
},
"GlossSee": "markup"
}
}
}
}
}

View File

@ -0,0 +1 @@
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]

View File

@ -0,0 +1 @@
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]

View File

@ -0,0 +1,8 @@
{
"name": "Jack (\"Bee\") Nimble",
"format": {"type": "rect",
"width": 1920,
"height": 1080,
"interlace": false,"frame rate": 24
}
}

View File

@ -0,0 +1,10 @@
{
"name": "Jack (\"Bee\") Nimble",
"format": {
"type": "rect",
"width": 1920,
"height": 1080,
"interlace": false,
"frame rate": 24
}
}

View File

@ -0,0 +1,11 @@
{"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}}

View File

@ -0,0 +1,18 @@
{
"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [{
"value": "New",
"onclick": "CreateNewDoc()"
}, {
"value": "Open",
"onclick": "OpenDoc()"
}, {
"value": "Close",
"onclick": "CloseDoc()"
}]
}
}
}

View File

@ -0,0 +1,26 @@
{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}}

View File

@ -0,0 +1,28 @@
{
"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}
}

View File

@ -0,0 +1,88 @@
{"web-app": {
"servlet": [
{
"servlet-name": "cofaxCDS",
"servlet-class": "org.cofax.cds.CDSServlet",
"init-param": {
"configGlossary:installationAt": "Philadelphia, PA",
"configGlossary:adminEmail": "ksm@pobox.com",
"configGlossary:poweredBy": "Cofax",
"configGlossary:poweredByIcon": "/images/cofax.gif",
"configGlossary:staticPath": "/content/static",
"templateProcessorClass": "org.cofax.WysiwygTemplate",
"templateLoaderClass": "org.cofax.FilesTemplateLoader",
"templatePath": "templates",
"templateOverridePath": "",
"defaultListTemplate": "listTemplate.htm",
"defaultFileTemplate": "articleTemplate.htm",
"useJSP": false,
"jspListTemplate": "listTemplate.jsp",
"jspFileTemplate": "articleTemplate.jsp",
"cachePackageTagsTrack": 200,
"cachePackageTagsStore": 200,
"cachePackageTagsRefresh": 60,
"cacheTemplatesTrack": 100,
"cacheTemplatesStore": 50,
"cacheTemplatesRefresh": 15,
"cachePagesTrack": 200,
"cachePagesStore": 100,
"cachePagesRefresh": 10,
"cachePagesDirtyRead": 10,
"searchEngineListTemplate": "forSearchEnginesList.htm",
"searchEngineFileTemplate": "forSearchEngines.htm",
"searchEngineRobotsDb": "WEB-INF/robots.db",
"useDataStore": true,
"dataStoreClass": "org.cofax.SqlDataStore",
"redirectionClass": "org.cofax.SqlRedirection",
"dataStoreName": "cofax",
"dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
"dataStoreUser": "sa",
"dataStorePassword": "dataStoreTestQuery",
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
"dataStoreInitConns": 10,
"dataStoreMaxConns": 100,
"dataStoreConnUsageLimit": 100,
"dataStoreLogLevel": "debug",
"maxUrlLength": 500}},
{
"servlet-name": "cofaxEmail",
"servlet-class": "org.cofax.cds.EmailServlet",
"init-param": {
"mailHost": "mail1",
"mailHostOverride": "mail2"}},
{
"servlet-name": "cofaxAdmin",
"servlet-class": "org.cofax.cds.AdminServlet"},
{
"servlet-name": "fileServlet",
"servlet-class": "org.cofax.cds.FileServlet"},
{
"servlet-name": "cofaxTools",
"servlet-class": "org.cofax.cms.CofaxToolsServlet",
"init-param": {
"templatePath": "toolstemplates/",
"log": 1,
"logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
"logMaxSize": "",
"dataLog": 1,
"dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
"dataLogMaxSize": "",
"removePageCache": "/content/admin/remove?cache=pages&id=",
"removeTemplateCache": "/content/admin/remove?cache=templates&id=",
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
"lookInContext": 1,
"adminGroupID": 4,
"betaServer": true}}],
"servlet-mapping": {
"cofaxCDS": "/",
"cofaxEmail": "/cofaxutil/aemail/*",
"cofaxAdmin": "/admin/*",
"fileServlet": "/static/*",
"cofaxTools": "/tools/*"},
"taglib": {
"taglib-uri": "cofax.tld",
"taglib-location": "/WEB-INF/tlds/cofax.tld"}}}

View File

@ -0,0 +1,94 @@
{
"web-app": {
"servlet": [{
"servlet-name": "cofaxCDS",
"servlet-class": "org.cofax.cds.CDSServlet",
"init-param": {
"configGlossary:installationAt": "Philadelphia, PA",
"configGlossary:adminEmail": "ksm@pobox.com",
"configGlossary:poweredBy": "Cofax",
"configGlossary:poweredByIcon": "/images/cofax.gif",
"configGlossary:staticPath": "/content/static",
"templateProcessorClass": "org.cofax.WysiwygTemplate",
"templateLoaderClass": "org.cofax.FilesTemplateLoader",
"templatePath": "templates",
"templateOverridePath": "",
"defaultListTemplate": "listTemplate.htm",
"defaultFileTemplate": "articleTemplate.htm",
"useJSP": false,
"jspListTemplate": "listTemplate.jsp",
"jspFileTemplate": "articleTemplate.jsp",
"cachePackageTagsTrack": 200,
"cachePackageTagsStore": 200,
"cachePackageTagsRefresh": 60,
"cacheTemplatesTrack": 100,
"cacheTemplatesStore": 50,
"cacheTemplatesRefresh": 15,
"cachePagesTrack": 200,
"cachePagesStore": 100,
"cachePagesRefresh": 10,
"cachePagesDirtyRead": 10,
"searchEngineListTemplate": "forSearchEnginesList.htm",
"searchEngineFileTemplate": "forSearchEngines.htm",
"searchEngineRobotsDb": "WEB-INF/robots.db",
"useDataStore": true,
"dataStoreClass": "org.cofax.SqlDataStore",
"redirectionClass": "org.cofax.SqlRedirection",
"dataStoreName": "cofax",
"dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
"dataStoreUser": "sa",
"dataStorePassword": "dataStoreTestQuery",
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
"dataStoreInitConns": 10,
"dataStoreMaxConns": 100,
"dataStoreConnUsageLimit": 100,
"dataStoreLogLevel": "debug",
"maxUrlLength": 500
}
}, {
"servlet-name": "cofaxEmail",
"servlet-class": "org.cofax.cds.EmailServlet",
"init-param": {
"mailHost": "mail1",
"mailHostOverride": "mail2"
}
}, {
"servlet-name": "cofaxAdmin",
"servlet-class": "org.cofax.cds.AdminServlet"
}, {
"servlet-name": "fileServlet",
"servlet-class": "org.cofax.cds.FileServlet"
}, {
"servlet-name": "cofaxTools",
"servlet-class": "org.cofax.cms.CofaxToolsServlet",
"init-param": {
"templatePath": "toolstemplates/",
"log": 1,
"logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
"logMaxSize": "",
"dataLog": 1,
"dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
"dataLogMaxSize": "",
"removePageCache": "/content/admin/remove?cache=pages&id=",
"removeTemplateCache": "/content/admin/remove?cache=templates&id=",
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
"lookInContext": 1,
"adminGroupID": 4,
"betaServer": true
}
}],
"servlet-mapping": {
"cofaxCDS": "/",
"cofaxEmail": "/cofaxutil/aemail/*",
"cofaxAdmin": "/admin/*",
"fileServlet": "/static/*",
"cofaxTools": "/tools/*"
},
"taglib": {
"taglib-uri": "cofax.tld",
"taglib-location": "/WEB-INF/tlds/cofax.tld"
}
}
}

View File

@ -0,0 +1,27 @@
{"menu": {
"header": "SVG Viewer",
"items": [
{"id": "Open"},
{"id": "OpenNew", "label": "Open New"},
null,
{"id": "ZoomIn", "label": "Zoom In"},
{"id": "ZoomOut", "label": "Zoom Out"},
{"id": "OriginalView", "label": "Original View"},
null,
{"id": "Quality"},
{"id": "Pause"},
{"id": "Mute"},
null,
{"id": "Find", "label": "Find..."},
{"id": "FindAgain", "label": "Find Again"},
{"id": "Copy"},
{"id": "CopyAgain", "label": "Copy Again"},
{"id": "CopySVG", "label": "Copy SVG"},
{"id": "ViewSVG", "label": "View SVG"},
{"id": "ViewSource", "label": "View Source"},
{"id": "SaveAs", "label": "Save As"},
null,
{"id": "Help"},
{"id": "About", "label": "About Adobe CVG Viewer..."}
]
}}

View File

@ -0,0 +1,54 @@
{
"menu": {
"header": "SVG Viewer",
"items": [{
"id": "Open"
}, {
"id": "OpenNew",
"label": "Open New"
}, null, {
"id": "ZoomIn",
"label": "Zoom In"
}, {
"id": "ZoomOut",
"label": "Zoom Out"
}, {
"id": "OriginalView",
"label": "Original View"
}, null, {
"id": "Quality"
}, {
"id": "Pause"
}, {
"id": "Mute"
}, null, {
"id": "Find",
"label": "Find..."
}, {
"id": "FindAgain",
"label": "Find Again"
}, {
"id": "Copy"
}, {
"id": "CopyAgain",
"label": "Copy Again"
}, {
"id": "CopySVG",
"label": "Copy SVG"
}, {
"id": "ViewSVG",
"label": "View SVG"
}, {
"id": "ViewSource",
"label": "View Source"
}, {
"id": "SaveAs",
"label": "Save As"
}, null, {
"id": "Help"
}, {
"id": "About",
"label": "About Adobe CVG Viewer..."
}]
}
}

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type="text/css">
html, body, iframe { margin: 0; padding: 0; height: 100%; }
iframe { display: block; width: 100%; border: none; }
</style>
<title>Application Error</title>
</head>
<body>
<iframe src="//s3.amazonaws.com/heroku_pages/error.html">
<p>Application Error</p>
</iframe>
</body>
</html>

View File

@ -0,0 +1,22 @@
[
{
"precision": "zip",
"Latitude": 37.7668,
"Longitude": -122.3959,
"Address": "",
"City": "SAN FRANCISCO",
"State": "CA",
"Zip": "94107",
"Country": "US"
},
{
"precision": "zip",
"Latitude": 37.371991,
"Longitude": -122.026020,
"Address": "",
"City": "SUNNYVALE",
"State": "CA",
"Zip": "94085",
"Country": "US"
}
]

View File

@ -0,0 +1,19 @@
[{
"precision": "zip",
"Latitude": 37.7668,
"Longitude": -122.3959,
"Address": "",
"City": "SAN FRANCISCO",
"State": "CA",
"Zip": "94107",
"Country": "US"
}, {
"precision": "zip",
"Latitude": 37.371991,
"Longitude": -122.02602,
"Address": "",
"City": "SUNNYVALE",
"State": "CA",
"Zip": "94085",
"Country": "US"
}]

View File

@ -0,0 +1,13 @@
{
"Image": {
"Width": 800,
"Height": 600,
"Title": "View from 15th Floor",
"Thumbnail": {
"Url": "http:/*www.example.com/image/481989943",
"Height": 125,
"Width": "100"
},
"IDs": [116, 943, 234, 38793]
}
}

Some files were not shown because too many files have changed in this diff Show More