C源文件DataRetrans.c
#include "DataRetrans.h"
#include "server.h"
#include "rtc.h"
LNode *reMSGHead = NULL;
MUX_SEM_STR gListMux = {0,0};
#if MALLOC_SWITCH
void *pmalloc(uint32_t size) { return calloc(size, sizeof(uint8_t));}
malloc_fn Retrans_malloc = (malloc_fn)pmalloc;
free_fn Retrans_free = (free_fn)free;
#else
void* (*Retrans_malloc)(size_t sz) = pvPortMalloc;//malloc;
void (*Retrans_free)(void *ptr) = vPortFree;//free;
#endif
#define QUEUE_SIZE 20
/*初始化单链表*/
void InitList(LNode **HL)
{
*HL = NULL; //置单链表为空
}
/*删除单链表中所有节点,使其成为空表*/
void ClearList(LNode **HL)
{
LNode *cp; //cp(current pointer)指向当前待处理节点
LNode *np; //np(next pointer)指向cp的后继节点
cp = *HL; //表头指针赋给cp
while(cp != NULL){
np = cp->next;
Free_LNodeList(cp);//释放该节点内存
cp =np; //使下一个节点成为当前节点
}
*HL = NULL;
}
/*得到单链表的长度*/
uint16_t LengthList(LNode *HL)
{
uint16_t i=0;
while(HL != NULL){
i++;
HL = HL->next;
}
return i;
}
/*检查单链表是否为空*/
bool EmptyList(LNode *HL)
{
return HL==NULL;
}
/*得到单链表中第pos个节点中的元素*/ //节点序号从表头的序号1开始
ElemType GetList(LNode *HL, uint16_t pos) //pos>=1
{
if(pos < 1){
LOG_L("pos is Wrongful!\n");
return NULL;
}
uint16_t i= 0;
while(HL != NULL){
i++;
if(i == pos)
break;
HL = HL->next;
}
if(HL != NULL){ //不为空,说明已找到该节点
return HL->data;
}
else{
LOG_L("pos is out range!\n");//没找到
return NULL;
}
}
/*从链表中查找出给定值的第一个元素,并由item带回该值*/
bool FindListAndModifyFlg(LNode *HL, ElemType *item)
{
MuxSempTake(&gListMux);
if(item == NULL || *item == NULL){
LOG_L("item is NULL!\n");//没找到
MuxSempGive(&gListMux);
return false;
}
ElemType tp = *item;
while(HL != NULL){
if(tp->message.cmd == HL->data->message.cmd){ //根据具体比对项,修改此处代码
HL->data->flag = DATA_RETRANS_FLAG_OFF;
*item = HL->data; //由item带回该节点,item将指向新的节点
MuxSempGive(&gListMux);
return true;
}
else{
HL = HL->next;
}
}
MuxSempGive(&gListMux);
return false;
}
/*从链表中查找出给定值的第一个元素,并由item带回该值*/
bool FindListPos(LNode *HL, ElemType *item)
{
while(HL != NULL){
if(*item == HL->data){ //根据具体比对项,修改此处代码
*item = HL->data; //根据具体比对项,修改此处代码
return true;
}
else{
HL = HL->next;
}
}
return false;
}
/*更新链表中等于给定值的第一个元素*/
bool UpdateList(LNode *HL, const ElemType *item)
{
while(HL != NULL){
if(HL->data == *item) //根据具体比对项,修改此处代码
break;
else
HL = HL->next;
}
if(HL == NULL) //没有找到给定值的元素,无法更新
return false;
else{
HL->data = *item; //根据具体比对项,修改此处代码
return true;
}
}
/*向单链表中按给定条件插入一个元素*/
//表头节点的序号指定为1,pos==1,表示插入到表头
//pos必须大于等于-1;pos:0 按值寻找插入位置 pos:-1插入到表尾 pos:>0 插入到pos指定序号位置
bool InsertList(LNode **HL, ElemType item, int pos)
{
uint16_t num=0;
if(pos < -1){
LOG_L("pos is Wrongful!\n");
return false;
}
//MuxSempTake(&gListMux);
//为item元素建立新节点
LNode *newptr;
newptr = (LNode *)Retrans_malloc(sizeof(LNode));
if(newptr == NULL){
LOG_L("malloc newptr error.\n");
//MuxSempGive(&gListMux);
return CL_FAIL;
}
newptr->data = item;
//寻找新节点的插入位置
LNode *cp = *HL; //cp指向当前节点,即待查节点,初始为指向表头
LNode *ap = NULL; //ahead pointer 指向cp的前驱节点,初始化空
if(pos == 0){ //pos:0 按值寻找插入位置
while(cp != NULL){
if(item < cp->data) //根据具体比对内容,修改此处代码
break; //找到新元素的插入位置,退出循环
else{
ap = cp;
cp = cp->next;
}
}
}
else if(pos == -1){ //pos:-1插入到表尾
while(cp != NULL){
ap =cp;
cp = cp->next;
num++;
}
}
else{ //pos:>0 插入到pos指定序号位置
int i = 0;
while(cp != NULL){
i++;
if(i == pos){ //pos==1,表示插入到表头
break;
}
else{
ap = cp;
cp = cp->next;
}
}
if(cp == NULL && i <pos){ //ypl-教材应该有误,教材源码: (cp == NULL && i+1 <pos)
LOG_L("pos is not exist!\n");
return false;
}
}
//完成新节点的插入操作
if(ap == NULL){ //ap为空,表示,将要插入的节点位置就是表头--表头的前驱节点就是空
newptr->next = *HL;
*HL = newptr;
LOG_L("Insert HEAD OK!\n");
}
else{ //把新节点插入到非表头位置,即插入到ap和cp之间
newptr->next = cp; //cp如果为空,则表示插入到表尾
ap->next = newptr;
LOG_L("Insert pos=%d OK\n",num+1);
}
return true;
}
/*从单链表中删除符合给定条件的第一个元素*/
//表头节点的序号指定为1,pos==1,表示删除表头节点
//pos必须大于等于-1;pos:0 按值寻找待删节点 pos:-1待删节点是表尾 pos:>0 插入到pos指定序号位置
bool DeleteList(LNode **HL, ElemType *item, int pos)
{
//单链表为空,无法删除,返回假
if(*HL == NULL){
LOG_L("LIST is empty!\n");
return false;
}
//pos值小于-1,返回假
if(pos < -1){
LOG_L("pos's value is invalid!\n");
return false;
}
//寻找需要被删除的元素的节点
LNode *cp = *HL; //cp指向当前节点,即待查节点,初始为指向表头
LNode *ap = NULL; //ahead pointer 指向cp的前驱节点,初始化空
if(pos == 0){ //按值查找被删除节点
while(cp != NULL){
if(*item == cp->data) //根据具体比对内容,修改此处代码
break;
else{
ap = cp;
cp = cp->next;
}
}
if(cp == NULL){
LOG_L("pos's value can't find!\n");
return false;
}
}
else if(pos == -1){ //查找表尾节点
while(cp->next != NULL){
ap = cp;
cp = cp->next;
}
}
else{ //查找给定序号的节点
int i = 0;
while(cp != NULL){
i++;
if(i == pos){
break;
}
else{
ap = cp;
cp = cp->next;
}
}
if(cp == NULL){
LOG_L("pos is over lenth!\n");//指定序号越界超过链表实际长度
return false;
}
}
//删除cp指向的节点
if(ap == NULL){ //删除表头节点
#if 1
LNode *temp = *HL; //ypl-语法可能出错的备用方案
*HL = temp->next;
#else
*HL = (*HL)->next;//ypl-语法不标准,但仍旧能够编译过
#endif
}
else{ //删除非表头节点,也可能是表尾节点
ap->next = cp->next;
}
//回收被删除节点的存储空间
Free_LNodeList(cp);//ypl-实际上,要回收的数据还不止节点占用的空间,还有 Message_Unit下的data域
return true;
}
extern int proto_AnHuiIron_RepeatSendData(QMsg* pmsg);
/*遍历一个链表*/
void TraverseList(LNode **HL) //1秒轮询一次
{
LNode *cp = *HL; //cp指向当前节点,即待查节点,初始为指向表头
LNode *ap = NULL; //ahead pointer 指向cp的前驱节点,初始化空
int pos = HEAD_OF_TABLE; //链表节点序号 指向当前待操作的节点
bool allow_sendflag = true; //遍历节点时,允许发送该节点的标志 true:允许 false:不允许等待下次遍历
while(cp != NULL){
//查看标志位,以判断是否删除当前节点
if(DATA_RETRANS_FLAG_OFF == cp->data->flag){ //需要删除当前节点
ap = cp;
if(false == DeleteList(HL, NULL, pos))
return;
LOG_L("Delete pos=%d OK\n",pos);
if(pos == HEAD_OF_TABLE){
cp = *HL;//cp指向新的头结点
//pos = HEAD_OF_TABLE; //序号保持不变
}
else{
cp = ap->next;//cp节点已经被删除,cp的后继节点暂存在ap的next域中
//pos++; //序号保持不变
}
}
else{ //判断重发次数是否超过3次 超过三次,则回调提示超时,并删除节点;未超过,若重发间隔周期已到,则发送该节点数据,并更新节点timers域和timestamp域
if(cp->data->timers >= 3){
//超过三次,则回调提示超时,并删除节点
if(cp->data->callback)
cp->data->callback();
//LOG_L("ack overtime!\n");
//然后,删除节点
cp->data->flag = DATA_RETRANS_FLAG_OFF; //标记删除当前节点--该节点在下次轮询TraverseList时,将被删除
}
else{
//未超过3次,若重发间隔周期已到,且遍历链表时,靠前的节点没发过数据,则发送该节点数据,并更新节点timers域和timestamp域
if( (cp->data->timestamp + 10 < GetTimeStamp()) && (true == allow_sendflag) ){
//靠前的节点没发过数据,则发送该节点数据,
cp->data->timers++;
cp->data->timestamp = GetTimeStamp();
proto_AnHuiIron_RepeatSendData(cp->data);
allow_sendflag = false; //遍历的下一个节点的数据将暂不发送
}
}
ap = cp;
cp = cp->next;
pos++;
}
}
}
/*对单链表进行数据排序
*假定待排序的单链表由表头指针HL所指向,对节点值按照从小到大进行排序链接时,首先建立一个空的单链表,
*然后把HL中的每个节点取出来并按值依次插入到新建立的单链表中,最后由HL带回新建链表的表头指针
*/
void SortList(LNode **HL)
{
//建立一个反映排序结果的新单链表,并初始化为空
LNode *SL;
InitList(&SL);
//从待排序的HL单链表中依次取出每个节点,并按值由小到大插入到新单链表中
LNode *r=*HL; //r指向待取出排序的一个节点,初始化为HL表头节点
LNode *t; //t指向r的后继节点
LNode *cp; //指向已排序的新链表,初始化为表头SL
LNode *ap; //ap指向cp的前驱节点,初始化为空
while(r != NULL)
{
//为新插入的r节点在SL中顺序查找出插入位置
t = r->next; //t指向原链表r节点的后继节点
cp = SL; //指向已排序的新链表,初始化为表头SL
ap = NULL; //ap指向cp的前驱节点,初始化为空
while(cp != NULL)
{
if(r->data < cp->data)
break; //找到被插入点,退出循环
else{
ap = cp; //ap和cp均后移,实现有序链表中的每个节点依次顺序和原链表中r节点比较
cp = cp->next;
}
}
//实现插入操作
if(ap == NULL){ //把r节点插入到表头
r->next = SL;
SL = r;
}
else{ //把r节点插入到ap和cp之间
r->next = cp;
ap->next = r;
}
//使r指向原链表的下一个节点
r = t;
}
//返回链表排序结果
*HL = SL;
}
/*数据单元内存释放*/
void FreeQMsg(QMsg *pmsg)
{
if(pmsg != NULL)
{
if(pmsg->message.data != NULL)
{
Retrans_free(pmsg->message.data);
pmsg->message.data = NULL;
}
Retrans_free(pmsg);
pmsg = NULL;
}
}
/*数据节点内存释放*/
void Free_LNodeList(LNode *HL)
{
if(NULL != HL){
if(HL->data != NULL){
FreeQMsg(HL->data);
HL->data = NULL;
}
Retrans_free(HL);
HL = NULL;//这样操作,其实,对实参并没有影响
}
}
/**
* 485总线任务初始化
*/
void DataRetransInit(void)
{
InitList(&reMSGHead);
}
/*****************互斥锁----------begin************************/
int MuxSempTake(MUX_SEM_STR *pSemp)
{
if (pSemp->status) {
if (xTaskGetCurrentTaskHandle() == pSemp->pTaskHandle) {
//CL_LOG("same task take sem.\n");
pSemp->status++;
return CL_OK;
}else{
while (pSemp->status) {
Feed_WDT();
vTaskDelay(20);
//if (10 < pSemp->semTakeCnt) {
//CL_LOG("take sem cnt=%d,warging.\n",pSemp->semTakeCnt);
//}else{
pSemp->semTakeCnt++;
//}
}
}
}
pSemp->status++;
pSemp->semTakeCnt = 0;
pSemp->pTaskHandle = xTaskGetCurrentTaskHandle();
return CL_OK;
}
void MuxSempGive(MUX_SEM_STR *pSemp)
{
if (0 == pSemp->status) {
CL_LOG("status=%d,error.\n",pSemp->status);
return;
}
pSemp->status--;
}
/*****************互斥锁----------end************************/
头文件DataRetrans.h
#ifndef __DATA_RESTRANS_H_
#define __DATA_RESTRANS_H_
#include "includes.h"
#include <stdbool.h>
//typedef enum {FALSE = 0, TRUE = !FALSE } Bool;
typedef void (*timeout_callback) (void);
enum{
DATA_RETRANS_FLAG_ON = 1, //未收到应答的重发标志 1:正在重发 0:结束重发
DATA_RETRANS_FLAG_OFF = 0,
};
//pos必须大于等于-1;pos:1,表示插入到表头 pos:0 按值寻找插入位置 pos:-1插入到表尾 pos:>0 插入到pos指定序号位置
#define END_OF_TABLE (-1) //表尾
#define HEAD_OF_TABLE 1 //表头
//#pragma pack(1)
/**
* 消息单元
*/ /*重发数据区*/
typedef struct
{
uint16_t len; //数据长度
void * data; //数据缓存区
uint16_t msgId; //流水号
uint8_t cmd; //指令
}Message_Unit;
/**
* 队列数据单元
*/
typedef struct
{
/*重发数据区*/
Message_Unit message;
/*重发控制器*/
uint8_t flag; //未收到应答的重发标志 1:正在重发 0:结束重发
uint8_t timers; //已重发次数
uint32_t timestamp; //上次发送时间戳
timeout_callback callback; //超时回调函数
}QMsg;
typedef QMsg* ElemType;
struct _LNode{
ElemType data;
struct _LNode *next;
};
typedef struct _LNode LNode;
//#pragma pack()
extern LNode *reMSGHead;
/**
* 重传消息链表
*/
struct Repeat_Qmsg
{
QMsg *data;
struct Repeat_Qmsg *next;
};
#define MALLOC_SWITCH 1 //0
#if MALLOC_SWITCH
typedef void *(*malloc_fn)(size_t sz);
typedef void (*free_fn)(void *ptr);
extern malloc_fn Retrans_malloc;//malloc;
extern free_fn Retrans_free;//free;
#else
extern void *(*Retrans_malloc)(size_t sz);//malloc;
extern void (*Retrans_free)(void *ptr);//free;
#endif
/*从链表中查找出给定值的第一个元素,并由item带回该值*/
bool FindListAndModifyFlg(LNode *HL, ElemType *item);
/*向单链表中按给定条件插入一个元素*/ //表头节点的序号指定为1,pos==1,表示插入到表头
bool InsertList(LNode **HL, ElemType item, int pos); //pos必须大于等于-1;pos:0 按值寻找插入位置 pos:-1插入到表尾 pos:>0 插入到pos指定序号位置
/*数据单元内存释放*/
void FreeQMsg(QMsg *pmsg);
/*数据节点内存释放*/
void Free_LNodeList(LNode *HL);
/*遍历一个链表*/
void TraverseList(LNode **HL); //1秒轮询一次
int DataRetrans_SendMsg(Message_Unit message,timeout_callback callback);
void BswSrv_485TaskInit(void);
#endif
应用文件main.c
#define LOG_E(fmt,args...) do { /*if (system_info.printSwitch)*/ printf("E10 %s %s:(%d) "fmt,GetCurrentTime(), __func__, __LINE__, ##args); }while(0)
#define LOG_L(fmt,args...) do { /*if (system_info.printSwitch)*/ printf("L10 %s %s:(%d) "fmt,GetCurrentTime(), __func__, __LINE__, ##args); }while(0)
//查询数据是否需要重发或者取消重发
void DataRetransTraverseList(void) //1秒轮询一次
{
TraverseList(&reMSGHead);
}
//重发机制封装数据 调用链表节点插入InsertList()
int sendDataToDataRetrans(uint16_t sn, int16_t datalen, void* msg, uint8_t cmd, timeout_callback callback)
{
QMsg *pmsg = (QMsg *)Retrans_malloc(sizeof(QMsg));//如果申请失败,还需要进一步处理
if(pmsg == NULL)
{
LOG_E("cmd=0x%02X,malloc pmsg error.\n",cmd);
return CL_FAIL;
}
memset((void*)pmsg, 0, sizeof(QMsg));
pmsg->message.len = datalen;
pmsg->message.data = (void*)Retrans_malloc(pmsg->message.len);
if(pmsg->message.data == NULL)
{
LOG_E("cmd=0x%02X,malloc data error.\n",cmd);
FreeQMsg(pmsg);
return CL_FAIL;
}
memset(pmsg->message.data, 0, pmsg->message.len);
memcpy(pmsg->message.data, (void*)msg, pmsg->message.len);
pmsg->message.msgId = sn - 1;
pmsg->message.cmd = cmd;
pmsg->flag = DATA_RETRANS_FLAG_ON;
pmsg->callback = callback;
pmsg->timers = 0;
pmsg->timestamp = GetTimeStamp();
/*插入到重发链表*/
int ret = (InsertList(&reMSGHead, pmsg, END_OF_TABLE)==true) ? CL_OK : CL_FAIL;
if(ret != CL_OK)
{
FreeQMsg(pmsg);
}
return ret;
}
void timeout_OverTempAlarmReport_callback (void)
{
LOG_E("OverTempAlarmReport timeout!\n");//触发告警 或者 取消告警 等待平台应答超时
}
//超温告警上报 0xC3 ---发起主体-设备
int proto_AnHuiIron_OverTempAlarmReport_report(uint8_t state, uint8_t tmp)
{
PKT_ANHUI_IRON_STR *pkt = (void*)server_send_buff;
anhui_OverTempAlarmReport_t *msg = (void*)pkt->data;
MuxSempTake(&gProtoSendMux);
memset(server_send_buff, 0, sizeof(server_send_buff));
msg->state = state;
msg->temp = tmp;
/*先直接发送一次到平台*/
proto_AnhuiIron_SendDataToService(pkt, system_info.mqtt_sn++, sizeof(anhui_OverTempAlarmReport_t), ANHUI_C2S_OverTempAlarmReport);
/*再封装到重发链表,重发机制封装数据*/
int ret = sendDataToDataRetrans(system_info.mqtt_sn, sizeof(anhui_OverTempAlarmReport_t), (void*)msg, \
ANHUI_C2S_OverTempAlarmReport, timeout_OverTempAlarmReport_callback);
MuxSempGive(&gProtoSendMux);
return ret;
}
/*链表遍历TraverseList()时,调用底层数据重发到平台*/
int proto_AnHuiIron_RepeatSendData(QMsg* pmsg)
{
PKT_ANHUI_IRON_STR *pkt = (void*)server_send_buff;
LOG_L("in--\n");
MuxSempTake(&gProtoSendMux);
memset(server_send_buff, 0, sizeof(server_send_buff));
memcpy((void*)pkt->data, (void*)pmsg->message.data , pmsg->message.len);
/*发送到平台*/
proto_AnhuiIron_SendDataToService(pkt, pmsg->message.msgId , pmsg->message.len, pmsg->message.cmd);
MuxSempGive(&gProtoSendMux);
return CL_OK;
}
enum{
//超温告警上报 1-触发告警 0-取消告警
ANHUI_TRIGGER_ALARM = 1,
ANHUI_CANCEL_ALARM = 0,
};
enum{
ANHUI_C2S_EquipmentAlarmAndRecovery = 0xC1,//设备告警与恢复 0xC1
ANHUI_C2S_NetworkAbnormalAlarm = 0xC2,//网络异常告警 0xC2
ANHUI_C2S_OverTempAlarmReport = 0xC3,//超温告警上报 0xC3
ANHUI_C2S_SmokeAlarmReport = 0xC6,//烟雾告警上报 0xC6
ANHUI_S2C_SetTempAlarmValue = 0xC4,//设置温度告警值 0xC4
ANHUI_S2C_QueryTempAlarmValue = 0xC5,//查询温度告警值 0xC5
};
void main()
{
uint8_t secondOk=0;
QMsg reMsg;
QMsg *preMsg = &reMsg;
reMsg.message.cmd = ANHUI_C2S_OverTempAlarmReport;//先传递命令字段,以便后续重发链表里查询并清标志reMsg.flag
while(1){
vTaskDelay(1000);//延时1秒
secondOk++;
DataRetransTraverseList();
if(0 == (secondOk % 8)){
proto_AnHuiIron_OverTempAlarmReport_report(ANHUI_TRIGGER_ALARM ,25);//超温告警上报 0xC3
}
if(4 == (secondOk % 8)){
FindListAndModifyFlg(reMSGHead, &preMsg);//超温告警上报 0xC3 结束上报,标记删除节点
}
}
}