好友
阅读权限10
听众
最后登录1970-1-1
|
本帖最后由 TokeyJs 于 2026-5-8 21:22 编辑
C++半导体SECS/GEM开源库(E30目前还未实现,后续计划去实现)。
TinySECSGem (https://github.com/tokeyjs/TinySECSGem)是一个面向半导体设备端与上位机端的轻量级 Windows C++ SECS/HSMS 通信库(TCP通信底层使用HPSocket库)。
当前版本聚焦于通信层:
SEMI E37 / HSMS-SS 传输与会话行为
SEMI E5 / SECS-II 消息序列化、解析与 Item Tree 数据模型
SEMI E30 中的 GEM 行为特性仍处于规划阶段,当前版本尚未实现。TinySECSGem 适合用于构建 SECS 消息收发、集成原型、Host 侧工具和设备侧通信服务,但目前不具备完整 GEM 设备行为模型。
最近工作有涉及到SECS,发现CPP的开源库几乎没有,所以在空闲时间根据E37,E5等协议标准开发了此库,在代码完成差不多的时候还使用AI进行代码审查等辅助工作(目前AI真的好用呀,好多小bug都能发现,并且效率还高!)。
欢迎大佬给库提点建议!!!
主要功能:目前库版本已满足半导体SECS E37 E5标准,已经可以用来开发SECS项目使用,已经完成了通信底层数据解析与数据组装功能。其实现在未实现的E30标准也只是在此基础上增加一些应用层面的功能特性。
E5协议主要是数据结构的序列化与反序列化:本库将SECS数据body解析成一棵树的结构,实现E5标准中的ListItem、BinaryItem、ASCIIItem、BooleanItem、UInt4ByteItem等基础数据结构类型,这些Item都继承自BaseItem,不同数据类型的序列化与非序列化都由各自类进行实现。
下面贴一些BaseItem和ListItem类的代码:
[C++] 纯文本查看 复制代码 //=====BaseItem.h===
#pragma once
#include<vector>
#include<string>
#include<cstdint>
#include "TinySECSGem.h"
namespace TinySECSGem
{
class BinaryItem;
class BooleanItem;
class ASCIIItem;
class UnicodeItem;
class JIS8Item;
class Int1ByteItem;
class Int2ByteItem;
class Int4ByteItem;
class Int8ByteItem;
class UInt1ByteItem;
class UInt2ByteItem;
class UInt4ByteItem;
class UInt8ByteItem;
class Float4ByteItem;
class Float8ByteItem;
class ListItem;
/**
* @brief SECS-II数据项抽象基类
*
* 该类定义了SECS/GEM协议中所有SECS-II数据项的统一抽象接口,
* 用于表示标准SECS数据结构中的任意数据节点,包括基础类型、
* 数组类型以及复合结构(LIST)类型等。
*
* 所有具体SECS-II数据类型(如ASCII、U1、U2、LIST等)均继承自该类实现。
*/
class TinySECSGEM_API BaseItem
{
protected:
EnumSECSItemType m_eItemType; ///< SECS-II数据类型标识(如ASCII/U1/U2/LIST等)
public:
/**
* @brief 构造BaseItem对象
*/
BaseItem();
/**
* @brief 析构BaseItem对象
*/
virtual ~BaseItem();
/**
* @brief 获取当前数据项序列化后的总字节大小
* @Return 当前数据项占用的字节数(包含子节点)
*/
virtual size_t totalByteSize() const = 0;
/**
* @brief 获取当前数据项数组元素数量
* @return 数组元素个数
*/
virtual size_t GetArraySize() const = 0;
/**
* @brief 清空当前数据内容
*/
virtual void clear() = 0;
/**
* @brief 深拷贝当前数据项对象
* @return 返回新创建的对象副本(调用方负责释放内存)
*/
virtual BaseItem* clone() = 0;
/**
* @brief 将当前数据结构转换为可读字符串(调试用途)
* @Param frontStr 输出前缀(用于缩进格式化)
* @return 格式化后的字符串表示
*/
virtual std::string print(std::string frontStr = "") const = 0;
/**
* @brief 序列化当前数据项为SECS-II字节流
* @param buff 输出缓冲区
* @param buffSize 缓冲区大小
* @return 实际写入的字节数
*/
virtual int Serialization(BYTE* buff, size_t buffSize) = 0;
/**
* @brief 从SECS-II字节流反序列化数据项
* @param startBufPos 起始缓冲区指针
* @param endBufPos 结束缓冲区指针(buf不包括endBufPos)
* @param bOk 输出参数,表示解析是否成功
* @return 解析后的BaseItem对象指针(失败返回nullptr)
*/
static BaseItem* Deserialization(BYTE* startBufPos, BYTE* endBufPos, bool& bOk);
/**
* @brief 解析SECS-II数据项头部信息
*
* 用于解析数据项格式类型及长度信息(Item Header)
*
* @param bufStart 起始缓冲区指针
* @param bufEnd 结束缓冲区指针(buf不包括bufEnd位置)
* @param format 输出数据类型格式
* @param lengthByteSize 输出长度字段所占字节数
* @param lengthByte 输出数据长度值
* @return true 解析成功
* @return false 解析失败
*/
static bool DeserializationItemHeader(
BYTE* bufStart,
BYTE* bufEnd,
EnumSECSItemType& format,
size_t& lengthByteSize,
size_t& lengthByte);
/**
* @brief 序列化SECS-II数据项头部信息
*
* 将数据类型及长度信息编码为SECS-II Item Header格式
*
* @param bufStart 输出缓冲区起始位置
* @param bufEnd 输出缓冲区结束位置(buf不包括BufEnd)
* @param format 数据类型格式
* @param singleTypeSize 单个元素大小
* @param arraySize 数组元素数量
* @return 写入的字节数
*/
static int SerializationItemHeader(
BYTE* bufStart,
BYTE* bufEnd,
EnumSECSItemType format,
size_t singleTypeSize,
size_t arraySize);
/**
* @brief 获取当前数据项格式类型
* @return EnumSECSItemType 当前SECS-II数据类型
*/
EnumSECSItemType getFormat() const;
/**
* @brief 类型安全转换为指定派生类型
* @tparam T 目标类型
* @return 转换成功返回指针,否则返回nullptr
*/
template<typename T>
T* as() {
return dynamic_cast<T*>(this);
}
/**
* @brief const版本类型安全转换
*/
template<typename T>
const T* as() const {
return dynamic_cast<const T*>(this);
}
/**
* @brief 判断当前对象是否为指定类型
* @tparam T 目标类型
* @return true 类型匹配
* @return false 类型不匹配
*/
template<typename T>
bool is() const {
return dynamic_cast<const T*>(this) != nullptr;
}
};
}
//=====BaseItem.cpp===
#include "BaseItem.h"
#include "BinaryItem.h"
#include "BooleanItem.h"
#include "ASCIIItem.h"
#include "UnicodeItem.h"
#include "JIS8Item.h"
#include "Int1ByteItem.h"
#include "Int2ByteItem.h"
#include "Int4ByteItem.h"
#include "Int8ByteItem.h"
#include "UInt1ByteItem.h"
#include "UInt2ByteItem.h"
#include "UInt4ByteItem.h"
#include "UInt8ByteItem.h"
#include "Float4ByteItem.h"
#include "Float8ByteItem.h"
#include "ListItem.h"
#include "TopItem.h"
TinySECSGem::BaseItem::BaseItem()
: m_eItemType(EnumSECSItemType::ItemType_UNKNOW)
{
}
TinySECSGem::BaseItem::~BaseItem()
{
}
bool TinySECSGem::BaseItem::DeserializationItemHeader(BYTE* bufStart, BYTE* bufEnd, EnumSECSItemType& format, size_t& lengthByteSize, size_t& lengthByte)
{
size_t index = 0;
if (bufEnd <= bufStart || !bufStart || !bufEnd)
return false;
format = (EnumSECSItemType)(bufStart[index] >> 2);
lengthByteSize = (bufStart[index] & 0x3);
index++;
if ((bufEnd - bufStart) < (1 + lengthByteSize)
|| lengthByteSize == 0)
return false;
lengthByte = 0;
lengthByte = bufStart[index++];
if (lengthByteSize >= 2)
{
lengthByte <<= 8;
lengthByte |= bufStart[index++];
}
if (lengthByteSize == 3)
{
lengthByte <<= 8;
lengthByte |= bufStart[index++];
}
return true;
}
int TinySECSGem::BaseItem::SerializationItemHeader(BYTE* bufStart, BYTE* bufEnd, EnumSECSItemType format, size_t singleTypeSize, size_t arraySize)
{
if (!bufStart || !bufEnd || bufEnd - bufStart < 2)
return -1;
size_t index = 0;
BYTE lengthByteSize = 0;
unsigned int length = arraySize * singleTypeSize;
bufStart[index] = (BYTE)format;
bufStart[index] <<= 2;
if (length <= 0xFFU)
{
lengthByteSize = 1;
bufStart[index] |= lengthByteSize;
index++;
if (bufEnd - bufStart < 1 + lengthByteSize)
return -1;
bufStart[index++] = (length & 0xFFU);
}
else if (length <= 0xFFFFU)
{
lengthByteSize = 2;
bufStart[index] |= lengthByteSize;
index++;
if (bufEnd - bufStart < 1 + lengthByteSize)
return -1;
bufStart[index++] = ((length >> 8) & 0xFFU);
bufStart[index++] = (length & 0xFFU);
}
else if (length <= 0xFFFFFFU)
{
lengthByteSize = 3;
bufStart[index] |= lengthByteSize;
index++;
if (bufEnd - bufStart < 1 + lengthByteSize)
return -1;
bufStart[index++] = ((length >> 16) & 0xFFU);
bufStart[index++] = ((length >> 8) & 0xFFU);
bufStart[index++] = (length & 0xFFU);
}
else
{
return -1;
}
return 1 + lengthByteSize;
}
TinySECSGem::BaseItem* TinySECSGem::BaseItem::Deserialization(BYTE* startBufPos, BYTE* endBufPos, bool& bOk)
{
if (startBufPos > endBufPos)
{
bOk = false;
return nullptr;
}
if (startBufPos == endBufPos)
{
bOk = true;
return nullptr;
}
bOk = true;
BaseItem* item = TopItem::Deserialization(startBufPos, endBufPos);
if (!item)
{
bOk = false;
delete item;
return nullptr;
}
return item;
}
TinySECSGem::EnumSECSItemType TinySECSGem::BaseItem::getFormat() const
{
return m_eItemType;
}
// ===ListItem.h===
#pragma once
#include "BaseItem.h"
#include<vector>
#include<string>
#include<cstdint>
#include "BinaryItem.h"
#include "BooleanItem.h"
#include "ASCIIItem.h"
#include "UnicodeItem.h"
#include "JIS8Item.h"
#include "Int1ByteItem.h"
#include "Int2ByteItem.h"
#include "Int4ByteItem.h"
#include "Int8ByteItem.h"
#include "UInt1ByteItem.h"
#include "UInt2ByteItem.h"
#include "UInt4ByteItem.h"
#include "UInt8ByteItem.h"
#include "Float4ByteItem.h"
#include "Float8ByteItem.h"
namespace TinySECSGem {
/**
* @brief SECS-II LIST data item implementation
*
* Represents the LIST compound data structure in the SECS/GEM protocol.
* LIST is a variable-length container type for organizing multiple
* SECS-II data items. Supports nested structures (List of List),
* making it one of the core node types in the SECS data tree.
*/
class TinySECSGEM_API ListItem : public BaseItem
{
private:
std::vector<BaseItem*> m_data; ///< Child item container (owning raw pointers)
public:
/**
* @brief Construct an empty LIST
*/
ListItem();
/**
* @brief Construct a single-element LIST
* @param val Initial child item (ListItem takes ownership)
*/
ListItem(BaseItem* val);
/**
* @brief Copy constructor (deep copy)
* @param item Source LIST
*/
ListItem(const ListItem& item);
/**
* @brief Copy assignment (deep copy)
* @param item Source LIST
*/
ListItem& operator=(const ListItem& item);
/**
* @brief Assign a single child (clears current content, inserts val)
* @param val Item pointer (ListItem takes ownership)
*/
ListItem& operator=(BaseItem* val);
/**
* @brief Destructor, releases all child items
*/
~ListItem();
/**
* @brief Clone this LIST (deep copy)
* @return New LIST pointer (caller owns the memory)
*/
virtual BaseItem* clone();
/**
* @brief Compute total serialized byte size (including all children)
*/
virtual size_t totalByteSize() const;
/**
* @brief Serialize LIST to SECS-II byte stream
* @param buff Output buffer
* @param buffSize Buffer size
* @return Actual bytes written
*/
virtual int Serialization(BYTE* buff, size_t buffSize);
/**
* @brief Deserialize a LIST from byte stream
* @param bufStart Start buffer pointer
* @param bufEnd End buffer pointer (exclusive)
* @return Parsed ListItem pointer (nullptr on failure)
*/
static ListItem* Deserialization(BYTE* bufStart, BYTE* bufEnd);
/**
* @brief Convert to human-readable string (debug)
* @param frontStr Prefix for indentation
* @return Formatted string
*/
virtual std::string print(std::string frontStr = "") const;
/**
* @brief Get the number of child items
* @return Child count
*/
virtual size_t GetArraySize() const;
/**
* @brief Clear all child items and release memory
*/
virtual void clear();
/**
* @brief Append a child at the end
* @param val Item pointer (ListItem takes ownership)
*/
void push_back(BaseItem* val);
/**
* @brief Remove the last child
*/
void pop_back();
/**
* @brief Set the child at the specified index
*
* @param index Index (out-of-range inserts at end)
* @param val Value (old value is released)
*/
void set(BaseItem* val, size_t index = 0);
/**
* @brief Insert a child at the specified position
* @param pos Insertion index
* @param val Item pointer (ListItem takes ownership)
*/
void insert(size_t pos, BaseItem* val);
/**
* @brief Erase the child at the specified position
* @param pos Index to erase
*/
void erase(size_t pos);
/**
* @brief Get the child at the specified index, cast to target type
* @tparam T Target type
* @param index Index
* @return Cast pointer, or nullptr on failure
*/
template<typename T>
T* get(size_t index) const {
if (index >= GetArraySize()) return nullptr;
return m_data[index]->as<T>();
}
/**
* @brief Check whether the child at index is of the specified type
* @tparam T Target type
* @param index Index
* @return true if type matches
* @return false if type mismatch or out of bounds
*/
template<typename T>
bool isType(size_t index) const {
if (index >= GetArraySize()) return false;
return m_data[index]->is<T>();
}
};
}
//===ListItem.cpp===
#include "ListItem.h"
using namespace TinySECSGem;
ListItem::ListItem()
:BaseItem()
{
m_eItemType = EnumSECSItemType::ItemType_LIST;
}
ListItem::ListItem(BaseItem* val)
:BaseItem()
{
m_eItemType = EnumSECSItemType::ItemType_LIST;
push_back(val);
}
ListItem::ListItem(const ListItem& item)
{
m_eItemType = EnumSECSItemType::ItemType_LIST;
clear();
for (int i = 0; i < item.GetArraySize(); i++)
{
push_back(item.get<BaseItem>(i)->clone());
}
}
ListItem& ListItem::operator=(const ListItem& item)
{
if (this == &item)
return *this;
clear();
for (int i = 0; i < item.GetArraySize(); i++)
{
push_back(item.get<BaseItem>(i)->clone());
}
return *this;
}
ListItem& ListItem::operator=(BaseItem* val)
{
if (val == this)
return *this;
clear();
push_back(val);
return *this;
}
ListItem::~ListItem()
{
clear();
}
BaseItem* ListItem::clone()
{
ListItem* item = new ListItem();
item->clear();
for (int i = 0; i < GetArraySize(); i++)
{
item->push_back(m_data[i]->clone());
}
return item;
}
size_t ListItem::totalByteSize() const
{
size_t rtn = 1;
if (GetArraySize() <= 0xFFU)
rtn += 1;
else if (GetArraySize() <= 0xFFFFU)
rtn += 2;
else if (GetArraySize() <= 0xFFFFFFU)
rtn += 3;
else
rtn = 0;
for (int i = 0; i < GetArraySize(); i++)
rtn += m_data[i]->totalByteSize();
return rtn;
}
int ListItem::Serialization(BYTE* buff, size_t buffSize)
{
if (totalByteSize() > buffSize || totalByteSize() == 0)
return -1;
size_t index = 0;
int costSize = BaseItem::SerializationItemHeader(buff, buff + buffSize, m_eItemType, 1, GetArraySize());
if (costSize < 0)
return -1;
index += costSize;
for (int i = 0; i < GetArraySize(); i++)
{
const int childSize = m_data[i]->Serialization(buff + index, buffSize - index);
if (childSize <= 0)
return -1;
index += static_cast<size_t>(childSize);
}
return totalByteSize();
}
ListItem* ListItem::Deserialization(BYTE* bufStart, BYTE* bufEnd)
{
size_t index = 0;
EnumSECSItemType type;
size_t lengthByteSize = 0;
size_t lengthByte = 0;
if (!BaseItem::DeserializationItemHeader(bufStart, bufEnd, type, lengthByteSize, lengthByte))
return nullptr;
if (type != EnumSECSItemType::ItemType_LIST)
return nullptr;
if ((bufEnd - bufStart) < (1 + lengthByteSize + lengthByte))
return nullptr;
index += (1 + lengthByteSize);
size_t length = 0; // size
length = lengthByte;
ListItem* item = new ListItem();
for (int i = 0; i < length; i++)
{
BaseItem* val = nullptr;
EnumSECSItemType itemType;
size_t itemLengthByteSize = 0;
size_t itemLengthByte = 0;
if (!BaseItem::DeserializationItemHeader(bufStart + index, bufEnd, itemType, itemLengthByteSize, itemLengthByte))
{
delete item;
return nullptr;
}
switch (itemType)
{
case EnumSECSItemType::ItemType_ASCII:
{
val = ASCIIItem::Deserialization(bufStart + index, bufEnd);
break;
}
case EnumSECSItemType::ItemType_JIS:
{
val = JIS8Item::Deserialization(bufStart + index, bufEnd);
break;
}
case EnumSECSItemType::ItemType_UNICODE:
{
val = UnicodeItem::Deserialization(bufStart + index, bufEnd);
break;
}
case EnumSECSItemType::ItemType_BINARY:
{
val = BinaryItem::Deserialization(bufStart + index, bufEnd);
break;
}
case EnumSECSItemType::ItemType_BOOLEAN:
{
val = BooleanItem::Deserialization(bufStart + index, bufEnd);
break;
}
case EnumSECSItemType::ItemType_INT1:
{
val = Int1ByteItem::Deserialization(bufStart + index, bufEnd);
break;
}
case EnumSECSItemType::ItemType_INT2:
{
val = Int2ByteItem::Deserialization(bufStart + index, bufEnd);
break;
}
case EnumSECSItemType::ItemType_INT4:
{
val = Int4ByteItem::Deserialization(bufStart + index, bufEnd);
break;
}
case EnumSECSItemType::ItemType_INT8:
{
val = Int8ByteItem::Deserialization(bufStart + index, bufEnd);
break;
}
case EnumSECSItemType::ItemType_UINT1:
{
val = UInt1ByteItem::Deserialization(bufStart + index, bufEnd);
break;
}
case EnumSECSItemType::ItemType_UINT2:
{
val = UInt2ByteItem::Deserialization(bufStart + index, bufEnd);
break;
}
case EnumSECSItemType::ItemType_UINT4:
{
val = UInt4ByteItem::Deserialization(bufStart + index, bufEnd);
break;
}
case EnumSECSItemType::ItemType_UINT8:
{
val = UInt8ByteItem::Deserialization(bufStart + index, bufEnd);
break;
}
case EnumSECSItemType::ItemType_FLOAT4:
{
val = Float4ByteItem::Deserialization(bufStart + index, bufEnd);
break;
}
case EnumSECSItemType::ItemType_FLOAT8:
{
val = Float8ByteItem::Deserialization(bufStart + index, bufEnd);
break;
}
case EnumSECSItemType::ItemType_LIST:
{
val = Deserialization(bufStart + index, bufEnd);
break;
}
default:
break;
}
if (!val)
{
delete item;
return nullptr;
}
const size_t itemSize = val->totalByteSize();
if (itemSize == 0)
{
delete item;
return nullptr;
}
index += itemSize;
item->push_back(val);
}
return item;
}
std::string ListItem::print(std::string frontStr /*= ""*/) const
{
// "< L[2] 12 3 >"
std::string formatStr = frontStr + "< L";
formatStr += ("[" + std::to_string(GetArraySize()) + "]");
for (int i = 0; i < GetArraySize(); i++)
formatStr += ("\n" + m_data[i]->print(" " + frontStr));
formatStr += ((GetArraySize() > 0 ? "\n" + frontStr : " ") + ">");
return formatStr;
}
size_t ListItem::GetArraySize() const
{
return m_data.size();
}
void ListItem::clear()
{
for (int i = 0; i < GetArraySize(); i++)
{
delete m_data[i];
}
m_data.clear();
}
void ListItem::push_back(BaseItem* val)
{
m_data.push_back(val);
}
void ListItem::pop_back()
{
if (m_data.empty())
return;
delete m_data.back();
m_data.pop_back();
}
void ListItem::set(BaseItem* val, size_t index /*= 0*/)
{
if (index >= GetArraySize())
{
push_back(val);
}
else
{
delete m_data[index];
m_data[index] = val;
}
}
void ListItem::insert(size_t pos, BaseItem* val)
{
if (pos > m_data.size())
return;
m_data.insert(m_data.begin() + pos, val);
}
void ListItem::erase(size_t pos)
{
if (pos >= m_data.size())
return;
delete m_data[pos];
m_data.erase(m_data.begin() + pos);
}
|
免费评分
-
查看全部评分
|