最近写的上位机主页面 因为需要展示收到的报文信息,所以当收发量变大时,内存占用会越来越大,在软件测试的时候甚至会达到数GB,最后想用数据库把这些收到的数据暂先存起来,然后通过对数据库的读取,分页获取历史报文。
首先大概展示一下我的主窗口页面
一、数据库的读写
设计的大概思路就是设一个报文类,里面存有一个报文中应有的一些参数
首先创建一个新的类DatabaseManager 存放数据库的初始化以及写入的方法,然后在方法中便可以直接调用。
using Microsoft.Data.Sqlite;
using System;
using UMI供电分析.Common;
public class DatabaseManager
{
private readonly string _connectionString;// 声明一个只读字符串变量来保存数据库连接字符串
//其中dPath存放的数据库的位置和名称
public DatabaseManager(string dbPath)// 定义一个DatabaseManager类来管理数据库操作
{
_connectionString = $"Data Source={dbPath}";// 使用传入的数据库路径初始化连接字符串
InitializeDatabase();
}
private void InitializeDatabase()
{
using (var connection = new SqliteConnection(_connectionString))
{
connection.Open();
var command = connection.CreateCommand();
command.CommandText =
@"
CREATE TABLE IF NOT EXISTS Packets (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Preamble TEXT,
DestinationAddress TEXT,
SourceAddress TEXT,
PayloadData TEXT,
FrameCheckSequence TEXT,
Timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
);
";
command.ExecuteNonQuery(); // 执行SQL命令
}
}
public void InsertPacket(CustomPacket packet)
{
using (var connection = new SqliteConnection(_connectionString))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
var command = connection.CreateCommand();
command.CommandText =
@"
INSERT INTO Packets (Preamble, DestinationAddress, SourceAddress, PayloadData, FrameCheckSequence)
VALUES ($preamble, $destinationAddress, $sourceAddress, $payloadData, $frameCheckSequence);
";
command.Parameters.AddWithValue("$preamble", packet.StringPremble);
command.Parameters.AddWithValue("$destinationAddress", packet.StringDestinationAddress);
command.Parameters.AddWithValue("$sourceAddress", packet.StringSourceAddress);
command.Parameters.AddWithValue("$payloadData", packet.StringPayloadData);
command.Parameters.AddWithValue("$frameCheckSequence", packet.StringFrameCheckSequence);
command.ExecuteNonQuery();
transaction.Commit();
}
}
}
}
在C#中,using
语句用于确保在使用完资源后及时释放资源,以避免资源泄漏和提高性能。在数据库操作中,使用 using
语句可以确保数据库连接在使用完毕后被正确关闭和释放,从而避免连接资源被占用导致的性能问题或连接泄漏。
剩余的是一些数据库指令。
在接受报文的时候 若CRC校验正确,则添加加入数据库
二、分页处理数据
此处我在主窗口设置了加载更多报文 ,如若点击后,直接加载一个新的窗口,其中包括一个日期控件DatePacket,用于选择特定日子的报文
在这个VM中,我又设置了一个新的类来作为存储媒介接受报文,并在构造函数中创建了一个“媒介”对象
public class PacketDataModel
{
public string StringPremble { get; set; }
public string StringDestinationAddress { get; set; }
public string StringSourceAddress { get; set; }
public string StringPayloadData { get; set; }
public string StringFrameCheckSequence { get; set; }
public DateTime Timestamp { get; set; }
}
public HistoryPacketViewModel()//构造函数
{
Historypacket_db = new ObservableCollection<PacketDataModel>();
LoadDataFromDatabase(1);
//LoadUniqueDatesFromDatabase();
SelectedDate = DateTime.Now; // 初始化为当前日期
}
对上一页和下一页的代码设置
public ICommand PacketLastPage => new DelegateCommand(
LoadPreviousPage
);
private void LoadPreviousPage()
{
if (currentPage > 0)
{
Historypacket_db.Clear();
currentPage--;
LoadDataFromDatabase(currentPage);
}
}
public ICommand PacketNextPage => new DelegateCommand(
LoadNextPage
);
public void LoadNextPage()
{
Historypacket_db.Clear();
currentPage++;
LoadDataFromDatabase(currentPage);
}
此处在命令执行一开始就先清空了数组代码,然后再执行了相关的一系列操作,目的是释放内存
接下来是日期选择的相关代码
首先设置View的代码,主要是绑定内容
SelectedDate所绑定即为鼠标所选中的日期,接着再到VM中设定一下该变量属性,每次修改后,都要对VM中的变量进行修改,同时要更新一下新的页面加载
private DateTime selectedDate;
public DateTime SelectedDate
{
get { return selectedDate; }
set
{
SetProperty(ref selectedDate, value);
LoadDataFromDatabase(1); // 根据选定的日期加载数据
}
}
接下来是页面加载代码
其中
ORDER BY Id DESC
:按照Id列的值进行降序排序。LIMIT {PageSize} OFFSET {page * PageSize}
:限制返回的行数为PageSize,并根据当前页数page和PageSize来设置偏移量。
private void LoadDataFromDatabase(int page)
{
string connectionString = "Data Source=YourDatabaseFilePath.db";// 建立数据库连接字符串
using (var connection = new SqliteConnection(connectionString))// 使用SQLite连接对象,确保在作用域结束时自动释放资源
{
connection.Open();
string countQuery = $"SELECT COUNT(*) FROM Packets WHERE DATE(Timestamp) = '{SelectedDate.ToString("yyyy-MM-dd")}'";
string dataQuery = $"SELECT * FROM Packets WHERE DATE(Timestamp) = '{SelectedDate.ToString("yyyy-MM-dd")}' ORDER BY Id DESC LIMIT {PageSize} OFFSET {page * PageSize}";
using (var countCommand = new SqliteCommand(countQuery, connection))
{
totalPacketsCount = Convert.ToInt32(countCommand.ExecuteScalar());
}
// 计算总页数
int totalPages = (totalPacketsCount + PageSize - 1) / PageSize;
// 更新分页数据
// 这里你需要根据实际情况处理页数溢出等问题
currentPage = Math.Max(0, Math.Min(currentPage, totalPages - 1));
using (var command = new SqliteCommand(dataQuery, connection))
{
using (var reader = command.ExecuteReader())
{
ObservableCollection<PacketDataModel> newData = new ObservableCollection<PacketDataModel>();
while (reader.Read())
{
PacketDataModel packet = new PacketDataModel();
int stringPrembleIndex = reader.GetOrdinal("Preamble");
if (!reader.IsDBNull(stringPrembleIndex))
{
packet.StringPremble = reader.GetString(stringPrembleIndex);
}
int destinationAddressIndex = reader.GetOrdinal("DestinationAddress");
if (!reader.IsDBNull(destinationAddressIndex))
{
packet.StringDestinationAddress = reader.GetString(destinationAddressIndex);
}
int sourceAddressIndex = reader.GetOrdinal("SourceAddress");
if (!reader.IsDBNull(sourceAddressIndex))
{
packet.StringSourceAddress = reader.GetString(sourceAddressIndex);
}
int payloadDataIndex = reader.GetOrdinal("PayloadData");
if (!reader.IsDBNull(payloadDataIndex))
{
packet.StringPayloadData = reader.GetString(payloadDataIndex);
}
int frameCheckSequenceIndex = reader.GetOrdinal("FrameCheckSequence");
if (!reader.IsDBNull(frameCheckSequenceIndex))
{
packet.StringFrameCheckSequence = reader.GetString(frameCheckSequenceIndex);
}
int timestampIndex = reader.GetOrdinal("Timestamp");
if (!reader.IsDBNull(timestampIndex))
{
packet.Timestamp = reader.GetDateTime(timestampIndex);
}
Historypacket_db.Add(packet);
}
}
}
}
}
其中两个 SQL 查询语句 countQuery
和 dataQuery
,分别用于查询符合条件的记录总数和分页数据。