当前位置: 首页 > news >正文

WPF 上位机开发模板

WPF 上位机开发模板

WPF上位机开发模板,集成了基础操作菜单、海康视觉实时图像界面、串口通讯、网口通讯、主流PLC通讯、数据存储、图片存储、参数配置、权限管理、第三方webapi接口接入、数据追溯与查询等功能。

一、项目结构

WpfSupervisor/
├── Models/                  # 数据模型
│   ├── DeviceModels.cs
│   ├── ImageModel.cs
│   ├── LogModel.cs
│   ├── ParameterModel.cs
│   └── UserModel.cs
├── Services/                # 服务层
│   ├── Communication/
│   │   ├── ComService.cs
│   │   ├── EthernetService.cs
│   │   ├── PlcService.cs
│   │   └── WebApiService.cs
│   ├── Database/
│   │   ├── DatabaseService.cs
│   │   └── ImageStorage.cs
│   ├── HikVision/
│   │   └── HikVisionService.cs
│   ├── Security/
│   │   ├── AuthService.cs
│   │   └── PermissionService.cs
│   └── Utility/
│       ├── ConfigManager.cs
│       └── Logger.cs
├── ViewModels/              # 视图模型
│   ├── MainViewModel.cs
│   ├── CommunicationViewModel.cs
│   ├── ImageViewModel.cs
│   ├── ParameterViewModel.cs
│   └── UserViewModel.cs
├── Views/                   # 视图
│   ├── MainWindow.xaml
│   ├── CommunicationView.xaml
│   ├── ImageView.xaml
│   ├── ParameterView.xaml
│   └── LoginView.xaml
├── Helpers/                 # 辅助类
│   ├── RelayCommand.cs
│   └── EnumExtensions.cs
└── App.xaml.cs              # 应用程序入口

二、核心代码实现

1. 数据模型 (Models/)

// DeviceModels.cs
public class PlcDevice
{public string Id { get; set; }public string Name { get; set; }public string IpAddress { get; set; }public int Port { get; set; }public string Protocol { get; set; } // Modbus, S7, etc.
}public class SerialDevice
{public string Id { get; set; }public string Name { get; set; }public string PortName { get; set; }public int BaudRate { get; set; }public Parity Parity { get; set; }public int DataBits { get; set; }public StopBits StopBits { get; set; }
}// ImageModel.cs
public class CapturedImage
{public string Id { get; set; }public byte[] ImageData { get; set; }public DateTime CaptureTime { get; set; }public string DeviceId { get; set; }public string FilePath { get; set; }
}// LogModel.cs
public class SystemLog
{public string Id { get; set; }public DateTime Timestamp { get; set; }public string Level { get; set; } // Info, Warning, Errorpublic string Message { get; set; }public string UserId { get; set; }
}// ParameterModel.cs
public class SystemParameter
{public string Id { get; set; }public string Key { get; set; }public string Value { get; set; }public string Description { get; set; }public string Category { get; set; }
}// UserModel.cs
public class User
{public string Id { get; set; }public string Username { get; set; }public string PasswordHash { get; set; }public string FullName { get; set; }public string Role { get; set; } // Admin, Operator, etc.public DateTime LastLogin { get; set; }
}

2. 服务层 (Services/)

2.1 通信服务
// ComService.cs
public class ComService : IDisposable
{private SerialPort _serialPort;public event Action<string> DataReceived;public bool IsOpen => _serialPort?.IsOpen ?? false;public void Open(SerialDevice device){_serialPort = new SerialPort(device.PortName, device.BaudRate, device.Parity, device.DataBits, device.StopBits);_serialPort.DataReceived += (s, e) => {try{var data = _serialPort.ReadExisting();DataReceived?.Invoke(data);}catch (Exception ex){Logger.LogError($"串口数据接收错误: {ex.Message}");}};_serialPort.Open();}public void Close() => _serialPort?.Close();public void Send(string data) => _serialPort?.Write(data);public void Dispose() => Close();
}// EthernetService.cs
public class EthernetService : IDisposable
{private TcpClient _tcpClient;private NetworkStream _stream;public event Action<string> DataReceived;public bool IsConnected => _tcpClient?.Connected ?? false;public async Task ConnectAsync(string ipAddress, int port){_tcpClient = new TcpClient();await _tcpClient.ConnectAsync(ipAddress, port);_stream = _tcpClient.GetStream();_ = ReceiveDataAsync();}private async Task ReceiveDataAsync(){try{var buffer = new byte[1024];while (_stream != null && _stream.CanRead){int bytesRead = await _stream.ReadAsync(buffer, 0, buffer.Length);var data = Encoding.ASCII.GetString(buffer, 0, bytesRead);DataReceived?.Invoke(data);}}catch (Exception ex){Logger.LogError($"以太网数据接收错误: {ex.Message}");}}public void Send(string data){if (_stream == null || !_stream.CanWrite) return;var bytes = Encoding.ASCII.GetBytes(data);_stream.Write(bytes, 0, bytes.Length);}public void Disconnect() => _tcpClient?.Close();public void Dispose(){Disconnect();_stream?.Close();}
}// PlcService.cs (使用S7.Net库示例)
public class PlcService : IDisposable
{private S7.Net.PLC _plc;public event Action<string> DataReceived;public bool IsConnected => _plc?.IsConnected ?? false;public async Task ConnectAsync(PlcDevice device){_plc = new S7.Net.PLC(device.Protocol == "S7" ? S7.Net.CpuType.S71200 : S7.Net.CpuType.S7300, device.IpAddress, device.Port);await Task.Run(() => _plc.Open());}public async Task<T> ReadAsync<T>(string address){if (!IsConnected) throw new InvalidOperationException("PLC未连接");return await Task.Run(() => (T)_plc.Read(address));}public async Task WriteAsync<T>(string address, T value){if (!IsConnected) throw new InvalidOperationException("PLC未连接");await Task.Run(() => _plc.Write(address, value));}public void Dispose() => _plc?.Close();
}// WebApiService.cs
public class WebApiService
{private readonly HttpClient _httpClient;private readonly string _baseUrl;public WebApiService(string baseUrl){_baseUrl = baseUrl.EndsWith("/") ? baseUrl : baseUrl + "/";_httpClient = new HttpClient();}public async Task<T> GetAsync<T>(string endpoint){var response = await _httpClient.GetAsync(_baseUrl + endpoint);response.EnsureSuccessStatusCode();return await response.Content.ReadAsAsync<T>();}public async Task PostAsync<T>(string endpoint, object data){var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");var response = await _httpClient.PostAsync(_baseUrl + endpoint, content);response.EnsureSuccessStatusCode();}
}
2.2 数据库服务
 
// DatabaseService.cs
public class DatabaseService
{private readonly string _connectionString;private SQLiteConnection _connection;public DatabaseService(string dbPath){_connectionString = $"Data Source={dbPath};Version=3;";InitializeDatabase();}private void InitializeDatabase(){_connection = new SQLiteConnection(_connectionString);_connection.Open();// 创建表ExecuteNonQuery(@"CREATE TABLE IF NOT EXISTS SystemLogs (Id TEXT PRIMARY KEY,Timestamp TEXT,Level TEXT,Message TEXT,UserId TEXT);CREATE TABLE IF NOT EXISTS SystemParameters (Id TEXT PRIMARY KEY,Key TEXT,Value TEXT,Description TEXT,Category TEXT);CREATE TABLE IF NOT EXISTS Users (Id TEXT PRIMARY KEY,Username TEXT,PasswordHash TEXT,FullName TEXT,Role TEXT,LastLogin TEXT);");}public void ExecuteNonQuery(string sql, params object[] parameters){using (var cmd = new SQLiteCommand(sql, _connection)){for (int i = 0; i < parameters.Length; i++){cmd.Parameters.AddWithValue($"@p{i}", parameters[i]);}cmd.ExecuteNonQuery();}}public T ExecuteScalar<T>(string sql, params object[] parameters){using (var cmd = new SQLiteCommand(sql, _connection)){for (int i = 0; i < parameters.Length; i++){cmd.Parameters.AddWithValue($"@p{i}", parameters[i]);}return (T)cmd.ExecuteScalar();}}public DataTable ExecuteQuery(string sql, params object[] parameters){using (var cmd = new SQLiteCommand(sql, _connection)){for (int i = 0; i < parameters.Length; i++){cmd.Parameters.AddWithValue($"@p{i}", parameters[i]);}var adapter = new SQLiteDataAdapter(cmd);var table = new DataTable();adapter.Fill(table);return table;}}public void Dispose(){_connection?.Close();}
}// ImageStorage.cs
public class ImageStorage
{private readonly string _imageFolderPath;private readonly DatabaseService _dbService;public ImageStorage(string folderPath, DatabaseService dbService){_imageFolderPath = folderPath;Directory.CreateDirectory(_imageFolderPath);_dbService = dbService;}public async Task SaveImageAsync(CapturedImage image){// 保存到数据库var imageId = Guid.NewGuid().ToString();var parameters = new object[]{imageId,image.ImageData != null ? Convert.ToBase64String(image.ImageData) : null,image.CaptureTime.ToString("o"),image.DeviceId,image.FilePath};_dbService.ExecuteNonQuery(@"INSERT INTO Images (Id, Data, CaptureTime, DeviceId, FilePath)VALUES (@p0, @p1, @p2, @p3, @p4);", parameters);// 保存文件if (image.ImageData != null){var filePath = Path.Combine(_imageFolderPath, $"{imageId}.jpg");await File.WriteAllBytesAsync(filePath, image.ImageData);// 更新数据库中的文件路径_dbService.ExecuteNonQuery(@"UPDATE Images SET FilePath = @p0 WHERE Id = @p1;", filePath, imageId);}}public async Task<CapturedImage> GetImageAsync(string id){var row = _dbService.ExecuteQuery("SELECT * FROM Images WHERE Id = @p0;", id).Rows[0];return new CapturedImage{Id = row["Id"].ToString(),ImageData = row["Data"] != DBNull.Value ? Convert.FromBase64String(row["Data"].ToString()) : null,CaptureTime = DateTime.Parse(row["CaptureTime"].ToString()),DeviceId = row["DeviceId"].ToString(),FilePath = row["FilePath"]?.ToString()};}
}
2.3 海康视觉服务
// HikVisionService.cs
public class HikVisionService
{private readonly HttpClient _httpClient;private readonly string _baseUrl;private readonly string _username;private readonly string _password;public event Action<byte[]> ImageReceived;public HikVisionService(string baseUrl, string username, string password){_baseUrl = baseUrl.EndsWith("/") ? baseUrl : baseUrl + "/";_username = username;_password = password;_httpClient = new HttpClient();// 登录Login();}private void Login(){var loginData = new Dictionary<string, string>{{"action", "login"},{"user", _username},{"password", _password}};var content = new FormUrlEncodedContent(loginData);var response = _httpClient.PostAsync(_baseUrl + "login", content).Result;response.EnsureSuccessStatusCode();}public async Task StartRealTimeImage(){// 启动实时图像流var streamResponse = await _httpClient.GetAsync($"{_baseUrl}stream");streamResponse.EnsureSuccessStatusCode();var stream = await streamResponse.Content.ReadAsStreamAsync();// 处理图像流数据using (var reader = new BinaryReader(stream)){while (true){// 实际实现需要解析海康威视的流协议// 这里简化处理var buffer = reader.ReadBytes(1024);if (buffer.Length > 0){// 解码图像数据var imageData = DecodeHikVisionImage(buffer);ImageReceived?.Invoke(imageData);}}}}private byte[] DecodeHikVisionImage(byte[] buffer){// 实际实现需要根据海康威视的图像编码格式解码// 这里简化处理,直接返回原始数据return buffer;}
}
2.4 安全服务
 
// AuthService.cs
public class AuthService
{private readonly DatabaseService _dbService;public AuthService(DatabaseService dbService){_dbService = dbService;}public async Task<User> LoginAsync(string username, string password){var userRow = _dbService.ExecuteQuery("SELECT * FROM Users WHERE Username = @p0;", username).Rows[0];var user = MapUserFromRow(userRow);// 验证密码if (VerifyPassword(password, user.PasswordHash)){user.LastLogin = DateTime.UtcNow.ToString("o");await UpdateUserAsync(user);return user;}return null;}private bool VerifyPassword(string inputPassword, string storedHash){// 实际实现应使用安全的密码哈希验证// 这里简化处理return inputPassword == storedHash; }public async Task<User> GetUserAsync(string userId){var row = _dbService.ExecuteQuery("SELECT * FROM Users WHERE Id = @p0;", userId).Rows[0];return MapUserFromRow(row);}private User MapUserFromRow(DataRow row){return new User{Id = row["Id"].ToString(),Username = row["Username"].ToString(),PasswordHash = row["PasswordHash"].ToString(),FullName = row["FullName"].ToString(),Role = row["Role"].ToString(),LastLogin = row["LastLogin"]?.ToString()};}private async Task UpdateUserAsync(User user){_dbService.ExecuteNonQuery(@"UPDATE Users SET LastLogin = @p0 WHERE Id = @p1;",user.LastLogin, user.Id);}
}// PermissionService.cs
public class PermissionService
{private readonly DatabaseService _dbService;public PermissionService(DatabaseService dbService){_dbService = dbService;}public async Task<bool> HasPermissionAsync(string userId, string permission){// 从数据库查询用户权限var hasPermission = await _dbService.ExecuteScalarAsync<bool>("SELECT COUNT(*) > 0 FROM UserPermissions WHERE UserId = @p0 AND Permission = @p1;",userId, permission);return hasPermission;}public async Task<IEnumerable<string>> GetUserPermissionsAsync(string userId){var permissions = await _dbService.ExecuteQueryAsync("SELECT Permission FROM UserPermissions WHERE UserId = @p0;", userId);return permissions.Select(r => r["Permission"].ToString());}
}

3. 视图模型 (ViewModels/)

// MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{private readonly IEventAggregator _eventAggregator;private readonly AuthService _authService;private readonly PermissionService _permissionService;private object _currentView;private User _currentUser;public object CurrentView{get => _currentView;set { _currentView = value; OnPropertyChanged(); }}public User CurrentUser{get => _currentUser;private set { _currentUser = value; OnPropertyChanged(); }}public ICommand LoginCommand { get; }public ICommand LogoutCommand { get; }public MainViewModel(IEventAggregator eventAggregator,AuthService authService,PermissionService permissionService){_eventAggregator = eventAggregator;_authService = authService;_permissionService = permissionService;LoginCommand = new RelayCommand(Login);LogoutCommand = new RelayCommand(Logout, CanLogout);}private async void Login(){// 实际实现应显示登录对话框var loginView = new LoginView();if (loginView.ShowDialog() == true){var user = await _authService.LoginAsync(loginView.Username, loginView.Password);if (user != null){CurrentUser = user;CurrentView = new ShellViewModel(_eventAggregator, user).View;_eventAggregator.Publish(new UserLoggedInEvent(user));}}}private void Logout(){CurrentUser = null;CurrentView = new LoginView();_eventAggregator.Publish(new UserLoggedOutEvent());}private bool CanLogout() => CurrentUser != null;public event PropertyChangedEventHandler PropertyChanged;protected void OnPropertyChanged([CallerMemberName] string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}
}// CommunicationViewModel.cs
public class CommunicationViewModel : INotifyPropertyChanged
{private readonly ComService _comService;private readonly EthernetService _ethernetService;private readonly PlcService _plcService;private readonly WebApiService _webApiService;private SerialDevice _selectedSerialDevice;private PlcDevice _selectedPlcDevice;public ObservableCollection<SerialDevice> SerialDevices { get; } = new();public ObservableCollection<PlcDevice> PlcDevices { get; } = new();public SerialDevice SelectedSerialDevice{get => _selectedSerialDevice;set { _selectedSerialDevice = value; OnPropertyChanged();OpenSerialPort();}}public PlcDevice SelectedPlcDevice{get => _selectedPlcDevice;set { _selectedPlcDevice = value; OnPropertyChanged();ConnectPlc();}}public ICommand RefreshDevicesCommand { get; }public ICommand SendSerialCommand { get; }public ICommand ReadPlcCommand { get; }public ICommand WritePlcCommand { get; }public CommunicationViewModel(ComService comService,EthernetService ethernetService,PlcService plcService,WebApiService webApiService){_comService = comService;_ethernetService = ethernetService;_plcService = plcService;_webApiService = webApiService;RefreshDevicesCommand = new RelayCommand(RefreshDevices);SendSerialCommand = new RelayCommand(SendSerialData, CanSendSerial);ReadPlcCommand = new RelayCommand(ReadPlcData, CanReadPlc);WritePlcCommand = new RelayCommand(WritePlcData, CanWritePlc);LoadDevices();}

相关文章:

  • 动态规划求解leetcode300.最长递增子序列(LIS)详解
  • NdrpEmbeddedPointerUnmarshall函数分析之第二次循环处理第二部分DomainSid
  • 三维重建(二十)——思路整理与第一步的进行
  • MongoDB 入门使用教程
  • 算法习题-力扣446周赛题解
  • 关于调度策略的系统性解析与物流机器人应用实践
  • 机器学习基础理论 - 频率派 vs 贝叶斯派
  • 在 Ubuntu24.04 LTS 上 Docker 部署英文版 n8n 和 部署中文版 n8n-i18n-chinese
  • 新增Webhook通知功能,文档目录树展示性能优化,zyplayer-doc 2.5.1 发布啦!
  • SSE协议
  • 《数字图像处理(面向新工科的电工电子信息基础课程系列教材)》图4-2
  • 数据资产价值及其实现路径-简答题回顾
  • 什么是WebSocket?NGINX如何支持WebSocket协议?
  • 2025春季NC:3.1TheTrapeziumRule
  • 第十一章 多态
  • Linux下编译并打包MNN项目迁移至其他设备
  • RTMP 协议解析 1
  • 摸鱼屏保神器工具软件下载及使用教程
  • AIGC在游戏开发中的革命:自动化生成3A级游戏内容
  • Vue3 组件通信与插槽
  • 辽宁省信访局副局长于江调任辽宁省监狱管理局局长
  • QFII一季度现身超300家公司:持有南京银行市值最高,5家青睐立航科技
  • 涉李小龙形象商标被判定无效,真功夫:暂无更换计划
  • 女儿被偷拍后,一个父亲的战斗
  • 专访|攸佳宁:手机只是矛盾导火索,重要的是看见孩子的内心
  • 岭南非遗大IP来上海了,舞剧《英歌》在文化广场连演两场