DCA文件解析笔记

写在前面:文章内容为本人的学习过程,路过大神不喜勿喷!

一、背景条件

以.dca为后缀的文件并非标准化格式,其含义和用途因软件、领域或上下文而异,可能是游戏存档、音频资源等游戏相关文件,或是特定软件的配置项目文件、编译中间文件,也可能是数据库备份、配置自动化等数据文件,亦或是加密专有格式或用户误写的自定义扩展名。

此处我遇到的.dca文件是设备上抛出来的文件,其中包含了设备所有点位的连续值的关键信息。

二、代码部分

1、dcafilebinary.h头文件:这里的头文件内容应该是包含了解析.dca文件的关键方法和属性。按照我的理解,其实是具体解析.dca文件的DLL动态链接库中的一些关键方法的接口映射,具体的功能的写法,已经在DLL文件中实现,这里只是定义如何调用。

#pragma once
//-------------------------------------------------------
// Copyright (c) 1998 by General Electric Company, U.S.A.
// Published in only a limited, copyright sense, and all
// rights, including trade secret rights are reserved.
//-------------------------------------------------------

//-----------------------------------------------------------------------------
//     TITLE:         VMS DCA File I/O Class Declarations
//     FILE NAME:     DCAFileBinary.hxx
//     PREPARED BY:   Metal Industries Process Automation
//                    General Electric Company
//                    1501 Roanoke Blvd.
//                    SALEM, VIRGINIA
//     CUSTOMER:      STANDARD
//-----------------------------------------------------------------------------
//     REVISIONS:
//     level  review date  author        change content
//     -----  -----------  ------------  --------------------------------------
//     01.0   10/01/98     S.R. Pixley   HM Original for Hoogovens DSP
//     02.0   06/10/99     S.R. Pixley   Original NT Version
//     03.0   03/22/00     S.R. Pixley   no bool, bool free, int forever
//     04.0   12/12/02     S.R. Pixley   MString class addition to cpprtu
//     07.1   10/07/03     P.V.Vernekar  filenames case sensitive on linux
//     07.2   16-FEB-04    RLM           fix compiliation error for Linux/g++ porting
//
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// ABSTRACT:
//    This declaration file describes the data structures, and related classes
//    used by various data collection subsystems to manage vms generated
//    dca file information retrieval and storage.
//-----------------------------------------------------------------------------

#ifndef __DCAFILEBINARY_HXX__
#define __DCAFILEBINARY_HXX__
#include "dca.h"
#include <cstdio>

/*******************************************************************************/

// The CDCAFileBinary object knows a little about how to read or write a file.
class PADCAFileBinary
{
public:
    int tempa[2];
    FILE* DCAfp;    // DCA file 
    int tempb[2];
    DCAW_FileHeaderType m_FileHdr;
    DCAW_FileCollectionHeaderType m_CollHdr;
    char* m_pExtraHeader;
    int fstatus;

    // Construction, destruction
public:
    PADCAFileBinary();
    ~PADCAFileBinary();

    // Base functionality
public:
    int GetRecordsProcessed() { return(m_RecordsProcessed); }
    int GetBytesProcessed() { return(m_RecordsProcessed * m_RecordLen); }
    int IsOpen();
    int Close();

    int OpenForWrite(const char* Fname,
        DCAW_FileCollectionHeaderType* pCollHdr,
        int NumRecordsToBuffer,
        int ExtraHeaderLen,
        char* pExtraHeader);
    int WriteSignalHeader(DCAW_FileSignalHeaderType* pSigHdr);
    //int OpenAndWriteAllHeaders(const char *Fname, CDCACollConfig &CollConfig, int NumRecordsToBuffer, int ExtraHeaderLen=0, char *pExtraHeader=NULL);

    int WriteDataRecord(void* pBuff);
    int UpdateExtraHeader(int ExtraHeaderLen,
        char* pExtraHeader);

    int Flush(int UpdateHeader);

    int OpenForRead(const char* Fname);
    int ReadSignalHeader(DCAW_FileSignalHeaderType& SigHdr);
    int ReadDataRecord(void* pBuff);

    const char* GetLastErrorString() { return(""); }
    int GetLastErrorCode() { return(m_LastWindowsError); }

private:
    typedef enum {
        DCA_FILE_CLOSED,
        DCA_FILE_OPEN_WRITE,
        DCA_FILE_OPEN_READ
    } CurrentFileState;

    CurrentFileState m_State;
    int m_RecordLen;
    unsigned long m_BuffLenInRecords;   // Num Records to buffer for writes.
    unsigned long m_RecordsInBuffer;    // Num records remaining to be written.
    char* m_pBuff;                      // Write buffer.
    int m_RecordsProcessed;             // Num data recoreds read or written.
    int m_SigHeadersRead;

    int m_LastWindowsError;  // From GetLastError

    // Helpers
private:
    int WriteBytes(void* pBuff, int NumBytes);
    int ReadBytes(void* pBuff, int NumBytes);
    void StoreLastError(const char* Msg, int ErrorCode = -1);
};

/*******************************************************************************/

#endif

2、DCA.h头文件:这里的头文件内容应该是包含了一些关键常量参数以及一些结构体的参数组成。

#pragma once
#ifndef __DCA_H__
#define __DCA_H__

#ifdef __vms
#include <pthread.h>        // for timespecc typedef 
#define _TIMESPEC_DEFINED_
#define __timespec_defined
#pragma __environment save
#pragma message save
#pragma message disable check
#pragma nomember_alignment
#endif

#if defined (__linux__)
#if !defined (__int64)
#define __int64 long long
#endif
#endif

#ifdef ULONG_MAX
inline void Int64ToDwords(__int64 big, unsigned long& dw_high, unsigned long& dw_low)
{
	dw_high = 0;
	while (big > (__int64)(ULONG_MAX))
	{
		dw_high++;
		big -= (__int64)((__int64)ULONG_MAX + (__int64)1);
	}
	dw_low = (unsigned long)big;
}
inline void DwordsToInt64(unsigned long dw_high, unsigned long dw_low, __int64& big)
{
	big = dw_low;
	while (dw_high > 0)
	{
		big += (__int64)(ULONG_MAX)+(__int64)1;
		dw_high--;
	}
}
#endif /* defined ULONG_MAX */

#pragma pack(1)

#if !defined (_TIMESPEC_DEFINED_) && !defined (__timespec_defined)
struct timespecc {
	unsigned long tv_sec;    /* seconds */
	long          tv_nsec;   /* nano seconds */
};
#define _TIMESPEC_DEFINED_
#define __timespec_defined  /* for linux */
#endif 

#define DCA_SERVER_PORT 5320

#define DCA_FILE_MAGIC_STRING "DCAW Data File\032"
#define DCA_VARIABLE_FILE_MAGIC_STRING "DCAV Data File\032"

const int DCA_NewestFileRev = 1;
const int DCA_VariableFileRev = 1;

const int DCA_FileMagicLen = 16;   /* All lengths for strings include NULL. */
const int DCA_HostNameLen = 40;
const int DCA_NetNameLen = 20;
const int DCA_CollNameLen = 20;
const int DCA_CollDescriptionLen = 50;
const int DCA_SigNameLen = 34;
const int DCA_SigDescriptionLen = 50;
const int DCA_SigUnitsLen = 12;
const int DCA_SigDatatypeLen = 40;
const int DCA_MaxTokens = 10;
const int DCA_MaxErrorStringLen = 200;
const int DCA_MaxDirectoryNameLen = 200;

/* ////////////////////// Binary Data File definitions /////////////////////// */
typedef enum {
	UC_LIVE_DATA = 0, UC_CAPT_BUFF = 1, REC_PC_LIVE_DATA = 2, DRV_CAPT_BUFF = 3,
	TRIP_LOG = 4, TRIP_LOG_LIVE = 5, IO_CARD_CAPT_BUFF = 6, UNKNOWN = 99
} DataSource;

typedef enum { EGD_TIME_STAMP = 0, PC_TIME_STAMP = 1 } TimeStampSource;

typedef enum {
	DCAW_Creator_Anonymous = 0,
	DCAW_Creator_Recorder = 1,
	DCAW_Creator_CoilProcessor = 2,
	DCAW_Creator_CoilHistorian = 3
} DCAW_FileCreatorType;

typedef enum {
	DCAW_Collection_TimeBasedContinuous = 0,
	DCAW_Collection_UCCaptureBufferUpload = 1,
	DCAW_Collection_ISDrvCaptureBufferUpload = 2,
	DCAW_Collection_LengthBased = 3,
	DCAW_Collection_TripLog = 4,
	DCAW_Collection_TripLive = 5,
	DCAW_Collection_IOCardCaptureBufferUpload = 6
} DCAW_CollectionType;

extern "C" _declspec(dllexport) typedef struct {
	char  Magic[DCA_FileMagicLen];
	long  Rev;
	unsigned char FileType;
	long  TimeZone;
	char  TimeZoneName[3];
} DCAW_FileBaseHeaderType;

extern "C" _declspec(dllexport) typedef struct {
	timespecc Time;
	double X;
} DCAW_FileStartXType;

extern "C" _declspec(dllexport) typedef struct {
	timespecc Time;
	double X;
} DCAW_FileEndXType;

extern "C" _declspec(dllexport) typedef struct {
	DCAW_FileBaseHeaderType Base;
	DCAW_FileStartXType     Start;
	DCAW_FileEndXType       End;
} DCAW_FileHeaderType;

extern "C" _declspec(dllexport) typedef struct
{
	short	Creator;			/* DCAW_FileCreatorType */
	short	CollectionType;   /* DCAW_CollectionType */
	char	CollName[DCA_CollNameLen];
	char	CollDescription[DCA_CollDescriptionLen];
	long	PeriodInMilliseconds;
	char	NetName[DCA_NetNameLen];
	short	NumSignals;
	long	DataLineLength; /* How long an entire data record is. */
	long	StatusOffset;   /* Byte offset into the data record of the first status bits. */
	long	BytesToSkipToGetToFirstSignalHeader; /* allows custom stuff to be put in file */
} DCAW_FileCollectionHeaderType;

extern "C" _declspec(dllexport) typedef struct
{
	char	Name[DCA_SigDescriptionLen];
	char	Descrition[DCA_SigDescriptionLen];
	char	Units[DCA_SigUnitsLen];
	char	AsciiDataType[DCA_SigDatatypeLen];
	short	DataLen; /* Num bytes in one sample.  Corresponds to AsciiDataType */
	long	OffsetIntoDataLine;
	long	StatusOffsetIntoDataLine;
	unsigned char	StatusBitmask;
	double	dEngMin;
	double	dEngMax;
	double	dRawMin;
	double	dRawMax;
	double	dDeadband;
	long	User[4];  /* Coilp uses these.  Recorder writes 0.  Trender ignores. */
} DCAW_FileSignalHeaderType;

extern "C" _declspec(dllexport) typedef struct {
	timespecc TimeStamp;
	/* data goes here - packed according to data type. */
	/* status goes here - one bit for each signal. */
} DCAW_FileRecordHeaderType;

/* ////////////////////// End Binary Data File definitions /////////////////// */

/* /////////////////// Messages to and from recorder ///////////////// */


/*-------------------------------------------------------------------------
 This struct is used as a header for UDP requests and replies.  It is exactly
 the same as the SDI_STD_HEADER, but is defined here so that programs that just
 want to do a simple UDP request won't have to include the whole mess of goop
 associated with SDI.
---------------------------------------------------------------------------*/
typedef struct {
	long  type;         /* Type of message. */
	long  UserContext;  /* Put this context from message into reply */
	unsigned long size; /* Size of message including this header */
} DCAW_UDP_HEADER;

#ifndef SDI_MSGTYPE_RECD_CMD
#define SDI_MSGTYPE_RECD_CMD 34
#endif
typedef enum {
	DCA_RECD_CMD_NONE = 0,
	DCA_RECD_CMD_FLUSH = 1,
	DCA_RECD_CMD_LOAD_CONFIG = 2,
	DCA_RECD_CMD_STOP = 3,
	DCA_RECD_CMD_START = 4,
	DCA_RECD_CMD_GET_BASE_DIR = 5,
	DCA_RECD_CMD_SHUTDOWN = 6
} DCAW_RecdCmdCodeType;

typedef enum {
	RECD_STAT_No_Error = 0,
	RECD_STAT_IllegalCommandCode = 1,
	RECD_STAT_CollectionDoesNotExist = 2
} DCAW_RecdStatusType;

/*--------------------------------------------------------------------------
 This struct is used in messages to the Recorder.  When using TCP/IP, it
 follows the SDI_STD_HEADER header whenever the SDI Headers's TYPE is equal
 to SDI_MSGTYPE_RECD_CMD.  When using UDP/IP, this struct is follows the
 DCAW_UDP_HEADER.
 ---------------------------------------------------------------------------*/
typedef struct {
	long CommandCode;   /* DCAW_RecdCmdCodeType	*/
	char CollName[DCA_CollNameLen];
} DCAW_RecdCmdHdr;

/*--------------------------------------------------------------------------
 This struct is used in messages from the Recorder.  When using TCP/IP, it
 follows the SDI_STD_HEADER header whenever the SDI Headers's TYPE is equal
 to SDI_MSGTYPE_RECD_CMD.  When using UDP/IP, this struct is follows the
 DCAW_UDP_HEADER.
---------------------------------------------------------------------------*/
typedef struct {
	long CommandCode; /* DCAW_RecdCmdCodeType */
	long Status;	  /* DCAW_RecdStatusType */
	char ErrorString[DCA_MaxErrorStringLen];
} DCAW_RecdRplyHdr;

/*--------------------------------------------------------------------------
These structs define the entire request and the entire reply for UDP messages.
---------------------------------------------------------------------------*/
typedef struct {
	DCAW_UDP_HEADER UdpHdr;
	DCAW_RecdCmdHdr Cmd;
}DCAW_UDP_Request;
typedef struct {
	DCAW_UDP_HEADER  UdpHdr;
	DCAW_RecdRplyHdr Rply;
}DCAW_UDP_Reply;

/*--------------------------------------------------------------------------
 This struct follows the DCAW_RecdRplyHdr in the reply to
 the DCA_RECD_CMD_GET_BASE_DIR command.  The DirName is 0 terminated
 and does NOT contain a trailing backslash character.
---------------------------------------------------------------------------*/
typedef struct {
	char DirName[DCA_MaxDirectoryNameLen];
} DCAW_RecdDirReply;

#pragma pack()

#ifdef __DECCXX
#pragma __environment restore
#pragma message restore
#endif

#endif

3、DACAParserServer.cpp文件:这个文件中的内容是具体调用方法以及将方法调用封装成Socket服务形式,将此服务于.dca文件放在同一台机器上,客户端发送文件的绝对路径,则返回解析后的文件内容的Json格式。

#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <string>
#include <regex>
#pragma comment(lib, "ws2_32.lib")

#include <objbase.h>	// GUID 用
#include <iostream>
#include <string>
#include <ctime>
#include "DCA.h"
#include <vector>
#include <iostream>
#include "dcafilebinary.h"
#include <map>
#include <mutex>
using namespace std;

#ifdef WIN32

#ifdef __cplusplus
#define DLL_EXPORT_C_DECL extern "C" __declspec(dllexport)
#define DLL_IMPORT_C_DECL extern "C" __declspec(dllimport)
#define DLL_EXPORT_DECL extern __declspec(dllexport)
#define DLL_IMPORT_DECL extern __declspec(dllimport)
#define DLL_EXPORT_CLASS_DECL __declspec(dllexport)
#define DLL_IMPORT_CLASS_DECL __declspec(dllimport)
#else
#define DLL_EXPORT_DECL __declspec(dllexport)
#define DLL_IMPORT_DECL __declspec(dllimport)
#endif

#else

#ifdef __cplusplus
#define DLL_EXPORT_C_DECL extern "C"
#define DLL_IMPORT_C_DECL extern "C"
#define DLL_EXPORT_DECL extern
#define DLL_IMPORT_DECL extern
#define DLL_EXPORT_CLASS_DECL
#define DLL_IMPORT_CLASS_DECL
#else
#define DLL_EXPORT_DECL extern
#define DLL_IMPORT_DECL extern
#endif

#endif


#define BUFFERSIZE 200
#define DATA_STR_LENGTH 15

/**
 * DCAFile
 */
typedef struct DCAFile
{
public:
	int tempa[2];
	int tempb[2];
	int fstatus;
	const char* m_pExtraHeader;
	int RecordsProcessed;
	int BytesProcessed;
	int NumSignals;

	DCAW_FileHeaderType m_FileHdr;
	DCAW_FileCollectionHeaderType m_CollHdr;

	DCAW_FileSignalHeaderType** SignalHeaders;
	float** Data;

	std::string ErrorMessage;
} DCAFile;


/**
 *  @fn     CreateGuidString()
 *  @brief  生成GUID。
 *  @return GUID字符串,带横杠。
 */
std::string CreateGuidString()
{
	char buf[64] = { 0 };
	GUID guid;
	if (S_OK == ::CoCreateGuid(&guid))
	{
		sprintf_s(buf, sizeof(buf)
			, "%08X-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X"
			, guid.Data1
			, guid.Data2
			, guid.Data3
			, guid.Data4[0], guid.Data4[1]
			, guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5]
			, guid.Data4[6], guid.Data4[7]
		);
	}

	return buf;
}

/**
 *  @fn     DeleteDCA()
 *  @brief  释放 DCAFile 对象。
 *  @return NONE。
 *
 */
void DeleteDCA(DCAFile* DCA)
{
	// 清理对象
	if (DCA == NULL) return;
	if (DCA->Data != NULL)
	{
		for (auto i = 0; i < DCA->NumSignals; i++)
		{
			free(DCA->Data[i]);
		}

		free(DCA->Data);
	}
	if (DCA->SignalHeaders != NULL)
	{
		for (auto i = 0; i < DCA->NumSignals; i++)
		{
			free(DCA->SignalHeaders[i]);
		}

		free(DCA->SignalHeaders);
	}
	delete DCA;
}

/**
 * @brief 把字符串的strsrc替换成strdst
 * @param strBig
 * @param strsrc
 * @param strdst
 * @return void
 */
void string_replace(std::string& strBig, const std::string& strsrc, const std::string& strdst)
{
	std::string::size_type pos = 0;
	std::string::size_type srclen = strsrc.size();
	std::string::size_type dstlen = strdst.size();

	while ((pos = strBig.find(strsrc, pos)) != std::string::npos)
	{
		strBig.replace(pos, srclen, strdst);
		pos += dstlen;
	}
}

/**
 * @brief 获取路径或URL的文件名(包括后缀,如 C:\Test\abc.xyz --> abc.xyz)
 * @param strFullName 路径
 * @return 文件名
 */
std::string GetPathOrURLShortName(std::string strFullName)
{
	if (strFullName.empty())
	{
		return "";
	}

	string_replace(strFullName, "/", "\\");

	std::string::size_type iPos = strFullName.find_last_of('\\') + 1;

	return strFullName.substr(iPos, strFullName.length() - iPos);
}
std::string escape_json(const std::string& s) {
	std::string result;
	for (char c : s) {
		switch (c) {
		case '\"': result += "\\\""; break;
		case '\\': result += "\\\\"; break;
		case '\b': result += "\\b"; break;
		case '\f': result += "\\f"; break;
		case '\n': result += "\\n"; break;
		case '\r': result += "\\r"; break;
		case '\t': result += "\\t"; break;
		default: result += c; break;
		}
	}
	return result;
};

/*
* 主要解析方法
*/
PADCAFileBinary binAssist;

std::vector<std::string> ParseDCA2Json(const char* filepath)
{
	std::vector<std::string> result;
	int ret = binAssist.OpenForRead(filepath);
	if (ret == 0)
	{
		return result;
	}

	std::string fileName = GetPathOrURLShortName(filepath);

	DCAFile* DCA = new DCAFile();
	ZeroMemory(DCA, sizeof(DCAFile));
	// 基本信息
	DCA->tempa[0] = binAssist.tempa[0];
	DCA->tempa[1] = binAssist.tempa[1];
	DCA->tempb[0] = binAssist.tempb[0];
	DCA->tempb[1] = binAssist.tempb[1];
	DCA->fstatus = binAssist.fstatus;
	DCA->m_pExtraHeader = binAssist.m_pExtraHeader == NULL ? "" : binAssist.m_pExtraHeader;
	DCA->RecordsProcessed = binAssist.GetRecordsProcessed();
	DCA->BytesProcessed = binAssist.GetBytesProcessed();
	DCA->NumSignals = binAssist.m_CollHdr.NumSignals;

	DCA->m_FileHdr = binAssist.m_FileHdr;
	DCA->m_CollHdr = binAssist.m_CollHdr;


	// 信号头
	DCA->SignalHeaders = static_cast<DCAW_FileSignalHeaderType**>(malloc(sizeof(DCAW_FileSignalHeaderType*) * binAssist.m_CollHdr.NumSignals));
	ZeroMemory(DCA->SignalHeaders, sizeof(DCAW_FileSignalHeaderType*) * binAssist.m_CollHdr.NumSignals);
	for (auto i = 0; i < binAssist.m_CollHdr.NumSignals; i++)
	{
		DCA->SignalHeaders[i] = static_cast<DCAW_FileSignalHeaderType*>(malloc(sizeof(DCAW_FileSignalHeaderType)));
		ZeroMemory(DCA->SignalHeaders[i], sizeof(DCAW_FileSignalHeaderType));
		binAssist.ReadSignalHeader(*(DCA->SignalHeaders[i]));
	}

	// 信号数据存储空间分配
	DCA->Data = static_cast<float**>(malloc(sizeof(float*) * binAssist.m_CollHdr.NumSignals));
	ZeroMemory(DCA->Data, sizeof(float*) * binAssist.m_CollHdr.NumSignals);
	for (auto i = 0; i < binAssist.m_CollHdr.NumSignals; i++)
	{
		DCA->Data[i] = static_cast<float*>(malloc(sizeof(float) * BUFFERSIZE));
		ZeroMemory(DCA->Data[i], sizeof(float) * BUFFERSIZE);
	}

	struct
	{
		timespecc TimeStamp;
		float data[2000];
	} RecordOfRead;

	int size = BUFFERSIZE;
	int pos = 0;

	// 读取信号数据
	do
	{
		// 分配的内存大小不够,则重新分配内存大小
		bool resize = false;
		bool failed = false;
		if (pos >= size)
		{
			resize = true;

			size += BUFFERSIZE;
			for (auto i = 0; i < binAssist.m_CollHdr.NumSignals; i++)
			{
				auto p = DCA->Data[i];
				DCA->Data[i] = static_cast<float*>(realloc(DCA->Data[i], sizeof(float) * size));
				if (DCA->Data[i] == NULL)
				{
					DCA->Data[i] = p;
					size -= BUFFERSIZE;
					failed = true;
					break;
				}
				ZeroMemory(DCA->Data[i] + size - BUFFERSIZE, BUFFERSIZE);
			}

		}

		// 如果重新分配内存,且分配失败了,则退出获取数据过程,并设置错误信息
		if (resize && failed)
		{
			DCA->ErrorMessage = "Heap can not offer more memory. Read file failed.";
			//binAssist.Close();
			DeleteDCA(DCA);
			return result;
		}

		ZeroMemory(&RecordOfRead, sizeof(RecordOfRead));
		binAssist.ReadDataRecord(&RecordOfRead);
		for (auto i = 0; i < binAssist.m_CollHdr.NumSignals; i++)
		{
			DCA->Data[i][pos] = RecordOfRead.data[i];
		}

		pos++;

	} while (!(RecordOfRead.TimeStamp.tv_sec == binAssist.m_FileHdr.End.Time.tv_sec && RecordOfRead.TimeStamp.tv_nsec == binAssist.m_FileHdr.End.Time.tv_nsec));


	// dca_signal_hdr 表
	for (int i = 0; i < DCA->NumSignals; i++)
	{
		char* pStrDatas = new char[size * DATA_STR_LENGTH];
		ZeroMemory(pStrDatas, size * DATA_STR_LENGTH);
		char* pPos = pStrDatas;

		for (int idx = 0; idx < pos; idx++)
		{
			char sData[DATA_STR_LENGTH];
			char* pPos_1 = sData;
			ZeroMemory(sData, DATA_STR_LENGTH);
			sprintf_s(sData, DATA_STR_LENGTH, "%11f", DCA->Data[i][idx]);
			while ((*pPos++ = (*pPos_1++)) != '\0') { /*NONE*/ }
			pPos--;
			*pPos++ = '|';
		}
		// 简单的字符串转义函数

		DCAW_FileSignalHeaderType* p = DCA->SignalHeaders[i];
		std::string id_signal_hdr = CreateGuidString();
		int nSqlLen = size * DATA_STR_LENGTH + 1000;
		char* sql_signal_hdr = (char*)malloc(sizeof(char) * nSqlLen);
		int ii = time(NULL);
		sprintf_s(sql_signal_hdr, nSqlLen,
			"{\"id_signal_hdr\": \"%s\", \"ii\": %d, \"fileName\": \"%s\", \"Start\": %f, \"End\": %f, \"Name\": \"%s\", \"Descrition\": \"%s\", \"Units\": \"%s\", \"AsciiDataType\": \"%s\", \"DataLen\": %d, \"OffsetIntoDataLine\": %d, \"StatusOffsetIntoDataLine\": %d, \"StatusBitmask\": %d, \"dEngMin\": %f, \"dEngMax\": %f, \"dRawMin\": %f, \"dRawMax\": %f, \"dDeadband\": %f, \"User0\": %d, \"User1\": %d, \"User2\": %d, \"User3\": %d, \"pStrDatas\": \"%s\"}",
			escape_json(id_signal_hdr).c_str(),
			ii,
			escape_json(fileName).c_str(),
			DCA->m_FileHdr.Start.X,
			DCA->m_FileHdr.End.X,
			escape_json(p->Name).c_str(),
			escape_json(p->Descrition).c_str(),
			escape_json(p->Units).c_str(),
			escape_json(p->AsciiDataType).c_str(),
			p->DataLen,
			p->OffsetIntoDataLine,
			p->StatusOffsetIntoDataLine,
			p->StatusBitmask,
			p->dEngMin,
			p->dEngMax,
			p->dRawMin,
			p->dRawMax,
			p->dDeadband,
			p->User[0],
			p->User[1],
			p->User[2],
			p->User[3],
			escape_json(pStrDatas).c_str()
		);
		string strSql = sql_signal_hdr;
		//std::cout << strSql << std::endl;
		//std::cout << i << std::endl;
		result.push_back(strSql);
		free(sql_signal_hdr);
	}

	//********************************************************************************************
	//binAssist.Close();
	DeleteDCA(DCA);
	return result;

}

// 从JSON字符串中提取关键数据
std::map<std::string, std::string> extractData(const std::string& jsonStr) {
	std::map<std::string, std::string> result;

	// 定义要提取的字段及其正则表达式模式
	std::vector<std::pair<std::string, std::string>> fields = {
		{"file_path", "\"file_path\":\\s*\"([^\"]+)\""},
		{"coil_id", "\"coil_id\":\\s*\"([^\"]+)\""},
		{"timestamp", "\"timestamp\":\\s*\"([^\"]+)\""}
	};

	// 对每个字段执行正则匹配
	for (const auto& field : fields) {
		std::regex pattern(field.second);
		std::smatch match;

		if (std::regex_search(jsonStr, match, pattern) && match.size() > 1) {
			result[field.first] = match[1].str();
		}
	}

	return result;
}

// 发送带长度前缀的消息
bool sendMessage(SOCKET socket, const std::string& data) {
	// 计算数据长度
	uint32_t length = static_cast<uint32_t>(data.size());

	// 准备长度字段(网络字节序,大端)
	uint32_t networkLength = htonl(length);

	// 发送长度字段
	if (send(socket, reinterpret_cast<const char*>(&networkLength), 4, 0) != 4) {
		std::cerr << "发送长度字段失败" << std::endl;
		return false;
	}

	// 发送数据体
	size_t bytesSent = 0;
	while (bytesSent < length) {
		int result = send(socket, data.c_str() + bytesSent, length - bytesSent, 0);
		if (result == SOCKET_ERROR) {
			std::cerr << "发送数据体失败" << std::endl;
			return false;
		}
		bytesSent += result;
	}

	// 发送结束符
	const char endChar = '\n';
	if (send(socket, &endChar, 1, 0) != 1) {
		std::cerr << "发送结束符失败" << std::endl;
		return false;
	}

	return true;
}


// 最大连接数限制
constexpr size_t MAX_CONNECTIONS = 10;

// 连接计数器和控制变量
std::atomic<size_t> activeConnections(0);
std::mutex connectionMutex;
std::condition_variable connectionCV;

// 处理单个客户端连接的函数
void handleClient(SOCKET clientSocket) {
	std::cout << "Client connected" << std::endl;

	while (true) {
		char recvbuf[512];
		int bytesReceived = recv(clientSocket, recvbuf, 512, 0);

		if (bytesReceived > 0) {
			// 成功接收数据
			std::cout << "Received: " << std::string(recvbuf, bytesReceived) << std::endl;

			// 处理接收到的数据
			const std::string received_str = std::string(recvbuf, bytesReceived);
			std::map<std::string, std::string> received_map = extractData(received_str);

			// 检查提取结果是否有效
			if (received_map.count("file_path") > 0 && received_map.count("coil_id") > 0) {
				// 使用std::string而不是const char*,避免悬空指针
				std::string filePath = received_map["file_path"];

				std::cout << "File Path: " << filePath << std::endl;

				// 调用函数
				std::vector<std::string> arr = ParseDCA2Json(filePath.c_str());
				arr.push_back("{\"END\": 1}");
				for (const auto& response_data : arr) {
					sendMessage(clientSocket, response_data);
				}
			}
			else {
				std::cerr << "Invalid JSON format, missing required fields" << std::endl;
			}
		}
		else if (bytesReceived == 0) {
			// 客户端正常关闭连接
			std::cout << "Client disconnected" << std::endl;

			break;
		}
		else {
			// 发生错误
			int error = WSAGetLastError();
			std::cerr << "recv failed with error: " << error << std::endl;

			// 检查是否是连接重置错误
			if (error == WSAECONNRESET) {
				std::cerr << "Connection reset by peer" << std::endl;
			}

			break;
		}
	}

	{
		std::lock_guard<std::mutex> lock(connectionMutex);
		--activeConnections;
	}
	connectionCV.notify_one();
	// 关闭客户端套接字
	closesocket(clientSocket);
}

// 服务器主函数
void server() {
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
		std::cerr << "WSAStartup failed" << std::endl;
		return;
	}

	SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (listenSocket == INVALID_SOCKET) {
		std::cerr << "socket failed" << std::endl;
		WSACleanup();
		return;
	}

	sockaddr_in serverAddr;
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_addr.s_addr = INADDR_ANY;
	serverAddr.sin_port = htons(12345);

	if (bind(listenSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
		std::cerr << "bind failed" << std::endl;
		closesocket(listenSocket);
		WSACleanup();
		return;
	}

	if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) {
		std::cerr << "listen failed" << std::endl;
		closesocket(listenSocket);
		WSACleanup();
		return;
	}

	std::cout << "Server started, waiting for connections..." << std::endl;

	// 持续接受新连接
	while (true) {
		SOCKET clientSocket = accept(listenSocket, NULL, NULL);
		if (clientSocket == INVALID_SOCKET) {
			std::cerr << "accept failed" << std::endl;
			continue; // 继续接受下一个连接
		}

		// 启动新线程处理客户端
		std::thread(handleClient, clientSocket).detach();
	}

	// 关闭监听套接字并清理Winsock
	closesocket(listenSocket);
	WSACleanup();
}

int main() {
	std::cout << "Start Server !\n";
	server();

}

4、除了上述文件内容外,还需要一些关键的dll动态链接库:

  • cpprtu70.dll
  • msvcp71.dll
  • msvcp140d.dll
  • ucrtbased.dll
  • vcruntime140d.dll

具体代码的具体含义,如果有解释错误的地方,还希望大家可以留言交流。。。

最后的最后:希望文章可以帮到大家,我们共同学习,加油吧!!!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值