C语言 单链表基本操作源码+数据重发机制应用

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 结束上报,标记删除节点
		}		
	}
}

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值