首次提交

This commit is contained in:
XingCheng3
2026-05-11 11:09:04 +08:00
commit 79c0f9cd43
260 changed files with 33504 additions and 0 deletions

317
SCADA/Core/AppConfig.cs Normal file
View File

@@ -0,0 +1,317 @@
using HslCommunication.WebSocket;
using Newtonsoft.Json;
using NPOI.POIFS.Properties;
using NPOI.SS.Formula.Functions;
using OpcBasic;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection.Emit;
using System.Threading;
using System.Windows.Forms;
using WebApi;
using static System.Configuration.ConfigurationManager;
namespace MesWork
{
/// <summary>
///
/// </summary>
public partial class Temp {
public static MesWorkForm mesWorkForm = null;
}
/// <summary>
/// 配置信息
/// </summary>
public partial class MesWorkForm
{
public static int workerThreads = 4000;
public static int completionPortThreads = 2000;
public static string BasicDataTableFile = AppSettings["BasicDataTableFile"];
public static int IsDemoMesServer = Convert.ToInt32(AppSettings["IsDemoMesServer"]);
public static int IsAllowWrite = Convert.ToInt32(AppSettings["IsAllowWrite"]);
public static int IsWriteMonitorLog = Convert.ToInt32(AppSettings["IsWriteMonitorLog"]);
public static int IsWriteLog4 = Convert.ToInt32(AppSettings["IsWriteLog4"]);
public static string ConnectionString = AppSettings["ConnectionString"];
public static string AuthorizationSet = AppSettings["AuthorizationSet"];
public static int WebApi_Port = Convert.ToInt32(AppSettings["WebApi_Port"]);
public static string WebApi_ReqUrl = AppSettings["WebApi_ReqUrl"];
/// <summary>
/// 当前工位号
/// </summary>
public static string Curr_Station = AppSettings["Curr_Station"] ?? "";
/// <summary>
/// 称重类型Ground=地面称重, Hanging=空中称重/吊称
/// </summary>
public static string WeighingType = AppSettings["WeighingType"] ?? "Hanging";
/// <summary>
/// 是否启用扫码枪1=启用, 0=禁用
/// </summary>
public static int IsUseBarcode = int.TryParse(AppSettings["IsUseBarcode"], out var _iub) ? _iub : 0;
/// <summary>
/// OP10扫码枪COM口如COM3空则不启用
/// </summary>
public static string OP10BarCOM = AppSettings["OP10BarCOM"] ?? "";
/// <summary>
/// OP20扫码枪COM口如COM4空则不启用
/// </summary>
public static string OP20BarCOM = AppSettings["OP20BarCOM"] ?? "";
/// <summary>
/// 扫码枪波特率
/// </summary>
public static int BarcodeBaudRate = int.TryParse(AppSettings["BarcodeBaudRate"], out var _bbr) ? _bbr : 9600;
/// <summary>
/// 设备工位名称列表(如 OP10, OP20在OnStart中从设备表加载
/// </summary>
public static System.Collections.Generic.List<string> StationOpNames = new System.Collections.Generic.List<string>();
/// <summary>
/// 工位显示名称映射opName → "OP10 — 称重位1"在OnStart中从设备表加载
/// </summary>
public static System.Collections.Generic.Dictionary<string, string> StationDisplayNames
= new System.Collections.Generic.Dictionary<string, string>(System.StringComparer.OrdinalIgnoreCase);
/// <summary>
/// 称重作业页面实例引用供MIS_Funtion推送交互消息
/// </summary>
public static Pages.UC_Weighing WeighingPage;
public static Color color_Faw = Color.FromArgb(0, 44, 101);//蓝色大众0,44,101
public static Color color_Good = Color.FromArgb(132, 185, 235);//浅蓝,自定义
public static Color color_Wait = Color.FromArgb(240, 230, 140);//淡黄,显示等待状态
public static Color color_Bad = SystemColors.ActiveBorder;//灰色,显示操作过程
public static Color color_Grey = Color.FromArgb(192, 192, 192);//灰色Table底色
public static Color color_Green = Color.FromArgb(22, 161, 136);//绿色
public static Color color_Yellow = Color.FromArgb(246, 227, 74);//黄色
public static Color color_Red = Color.FromArgb(255, 0, 0);//红色
public static Color color_Blue_FJ = Color.FromArgb(19, 34, 122);//深蓝
public static Color color_White_FJ = Color.White;//白色
public static Color color_Control = SystemColors.Control;//控件默认颜色
public static bool OnLine_Sql = false;
public static string Curr_UserName = "";
public static string Curr_LoginID = "";
public static string Curr_UserPassWord = "";
/// <summary>
/// WebApi
/// </summary>
public static InitServer initServer = null;
/// <summary>
/// 监控数据的所有变化,从这里产生
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public override void OPCTagData_TagDataOnChangeMIS(object sender, DeviceDriver_BasicData.CustomeEvetnArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(MIS_Funtion), e);
}
/// <summary>
/// 定时触发检测PLC在线状态周期3 s
/// </summary>
/// <param name="sender"></param>
/// <param name="eState"></param>
public override void PlcStateEvent(object sender, DeviceDriver_BasicData.PlcStateEvetnArgs eState)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(MIS_Device), eState);
}
/// <summary>
///
/// </summary>
/// <param name="t"></param>
/// <param name="location"></param>
/// <param name="content"></param>
public override void E_WriteLog(Enum_LogType t, string location, string content)
{
if (content .Contains("GetTagIDByTagTypeCodeID()"))
{
return;
}
WriteLog_Info(t.ToString(), $"<[<{location}>]>{content}");
}
/// <summary>
///
/// </summary>
[STAThread]
static void Main()
{
new System.Threading.Mutex(true, $"MES_PWT_A95", out bool bCanRun);
if (!bCanRun)
{
MessageBox.Show("MES采集系统 已经在运行!不可重复启动!");
return;
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(Temp.mesWorkForm = new MesWorkForm());
}
/// <summary>
///
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
private string Hello_You_All_Never_Know(string[] args)
{
return Guid.NewGuid().ToString() + Guid.NewGuid().ToString();
}
/// <summary>
/// 写入信号
/// </summary>
/// <param name="tagTypeCodeID"></param>
/// <param name="ststionName"></param>
/// <param name="value"></param>
public static void WritePLC_IF(int tagTypeCodeID,string ststionName,object value)
{
if (IsAllowWrite == 1)
{
WritePLC(tagTypeCodeID, ststionName, value);
}
}
/// <summary>
/// 写日志
/// 2023.10.30.llx
/// </summary>
public static void WriteLog_Info(string type, string msg, Exception ex = null)
{
if (IsWriteLog4 == 1)
{
MW_Log.LoggerHelper.Info(type, msg, ex);
}
}
private void CommunicationCheckOnLine()
{
new Thread(new ThreadStart(delegate ()
{
while (true)
{
try
{
MesWorkForm.OnLine_Sql = DataLinkMesWork.SQLCommon.ExecuteDataTable("select getdate()", ConnectionString, out DataTable dt, out string err);
}
catch (Exception) { }
Thread.Sleep(5000);
}
}))
{ IsBackground = true }.Start();
}
private void Loop_SelectAlarmShow()
{
while (true)
{
try
{
B_DB_Opera.Query_AlarmData(Curr_Station, out int count, out string text);
if (count > 0)
{
label_Alarm.Text = $"({count}) {text}";
panel_Alarm.BackColor = Color.Red;
}
else
{
label_Alarm.Text = $"";
panel_Alarm.BackColor = Color.FromArgb(30, 58, 95);
}
}
catch (Exception err)
{
}
Thread.Sleep(1000);
}
}
// Loop_ReadRealPageShow 已移除旧UI控件引用PLC信号显示将在 UC_Weighing 中实现
public void ShowSignal(Control c,string opName)
{
if (!c.Name.Contains("_Show_"))
{
return;
}
var tagTypeCodeID = Convert.ToInt32(c.Name.Split('_')[3]);
if (c is TextBox)
{
try
{
c.Text = PlcLinkForm.ReadPLC(tagTypeCodeID, opName).ToString();
}
catch (Exception err)
{
c.Text = "不存在该信号!";
}
}
}
public void ShowSignal(Control c, int tagTypeCodeID, string opName)
{
if (c is Panel)
{
try
{
c.BackColor = PlcLinkForm.ReadPLC(tagTypeCodeID, opName).ToString().ToLower().Replace("true", "1").Replace("false", "0") == "1" ? Color.Lime : color_Grey;
}
catch (Exception err)
{
c.BackColor = color_Grey;
}
}
}
public static ConcurrentDictionary<string, TagFormat> ReadPLC_OpName(string opName)
{
try
{
var res = new ConcurrentDictionary<string, TagFormat>();
var tagAdrListTagFormat0 = PlcLinkForm.ReadPLC_GroupData_SingleTag(opName);
foreach (var item in tagAdrListTagFormat0)
{
var key = item.Key;
var value = (TagFormat)item.Value;
res.TryAdd(key, value);
}
return res;
}
catch (Exception err)
{
return null;
}
}
}
}

View File

@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace MesWork
{
/// <summary>
/// 扫码枪管理器 — 管理最多2把扫码枪的生命周期和事件分发
/// </summary>
public static class BarcodeManager
{
/// <summary>
/// 扫码枪实例字典key = opNameOP10/OP20
/// </summary>
public static Dictionary<string, BarcodeScanner> Scanners { get; } = new Dictionary<string, BarcodeScanner>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// 是否启用扫码枪功能
/// </summary>
public static bool IsEnabled => MesWorkForm.IsUseBarcode == 1;
/// <summary>
/// 扫码事件回调(由外部设置,通常指向 MIS_Funtion 的处理方法)
/// </summary>
public static Action<string, string> OnBarcodeScanned;
/// <summary>
/// 初始化扫码枪(读取配置并创建实例)
/// </summary>
public static void Init()
{
if (!IsEnabled) return;
int baudRate = MesWorkForm.BarcodeBaudRate;
// OP10
string op10Com = MesWorkForm.OP10BarCOM?.Trim();
if (!string.IsNullOrEmpty(op10Com))
{
var scanner = new BarcodeScanner("OP10", baudRate);
scanner.BarcodeReceived += HandleBarcodeReceived;
scanner.Log += HandleLog;
Scanners["OP10"] = scanner;
scanner.Connect(op10Com);
}
// OP20
string op20Com = MesWorkForm.OP20BarCOM?.Trim();
if (!string.IsNullOrEmpty(op20Com))
{
var scanner = new BarcodeScanner("OP20", baudRate);
scanner.BarcodeReceived += HandleBarcodeReceived;
scanner.Log += HandleLog;
Scanners["OP20"] = scanner;
scanner.Connect(op20Com);
}
}
/// <summary>
/// 统一扫码事件处理
/// </summary>
private static void HandleBarcodeReceived(string opName, string barcode)
{
OnBarcodeScanned?.Invoke(opName, barcode);
}
/// <summary>
/// 扫码枪日志 → 系统日志 + 称重页面
/// </summary>
private static void HandleLog(string message)
{
MesWorkForm.WriteLog_Info("BarcodeScanner", message);
MesWorkForm.WeighingPage?.AppendLog(message);
}
/// <summary>
/// 手动发送条码(模拟扫码触发)
/// </summary>
public static void ManualSend(string opName, string barcode)
{
// 直接走事件回调,和自动扫码一样的路径
if (!string.IsNullOrWhiteSpace(barcode))
HandleBarcodeReceived(opName, barcode.Trim());
}
/// <summary>
/// 释放所有扫码枪
/// </summary>
public static void Shutdown()
{
foreach (var scanner in Scanners.Values)
{
try { scanner.Dispose(); } catch { }
}
Scanners.Clear();
}
/// <summary>
/// 获取所有可用COM口
/// </summary>
public static string[] GetAvailablePorts()
{
try { return System.IO.Ports.SerialPort.GetPortNames(); }
catch { return new string[0]; }
}
}
}

View File

@@ -0,0 +1,323 @@
using System;
using System.IO.Ports;
using System.Text;
using System.Threading.Tasks;
namespace MesWork
{
/// <summary>
/// 单把扫码枪的串口通信封装
/// 包含数据接收(缓冲+空闲提交)、防抖、断线自动重连
/// </summary>
public class BarcodeScanner : IDisposable
{
// ── 基本属性 ──
/// <summary>关联工位号OP10/OP20</summary>
public string OpName { get; }
/// <summary>当前COM口</summary>
public string ComPort { get; private set; }
/// <summary>是否已连接</summary>
public bool IsConnected
{
get { try { return _serial != null && _serial.IsOpen; } catch { return false; } }
}
/// <summary>最近一次扫到的条码</summary>
public string LastBarcode { get; private set; } = "";
/// <summary>最近一次扫码时间</summary>
public DateTime? LastScanTime { get; private set; }
// ── 事件 ──
/// <summary>扫码完成事件opName, barcode</summary>
public event Action<string, string> BarcodeReceived;
/// <summary>日志事件message由外部接入系统日志</summary>
public event Action<string> Log;
// ── 私有字段 ──
private SerialPort _serial;
private readonly int _baudRate;
private readonly object _bufferLock = new object();
private readonly StringBuilder _buffer = new StringBuilder(256);
private System.Windows.Forms.Timer _flushTimer;
private const int FlushIntervalMs = 60;
// 防抖
private string _lastDebounceCode = "";
private DateTime _lastDebounceTime = DateTime.MinValue;
private const int DebounceMs = 500;
// 断线重连
private System.Windows.Forms.Timer _reconnectTimer;
private volatile bool _reconnecting = false;
private bool _disposed = false;
public BarcodeScanner(string opName, int baudRate = 9600)
{
OpName = opName;
_baudRate = baudRate;
}
/// <summary>
/// 连接指定COM口
/// </summary>
public bool Connect(string comPort)
{
if (string.IsNullOrWhiteSpace(comPort) || comPort.Length < 3)
return false;
try
{
Disconnect();
ComPort = comPort;
_serial = new SerialPort
{
PortName = comPort,
BaudRate = _baudRate,
Parity = Parity.None,
StopBits = StopBits.One,
ReadTimeout = 1000,
DtrEnable = true
};
_serial.DataReceived += Serial_DataReceived;
_serial.Open();
// 空闲提交定时器
if (_flushTimer == null)
{
_flushTimer = new System.Windows.Forms.Timer { Interval = FlushIntervalMs };
_flushTimer.Tick += FlushTimer_Tick;
}
// 断线重连定时器每3秒检测
if (_reconnectTimer == null)
{
_reconnectTimer = new System.Windows.Forms.Timer { Interval = 3000 };
_reconnectTimer.Tick += ReconnectTimer_Tick;
}
_reconnectTimer.Start();
Log?.Invoke($"[{OpName}] 扫码枪已连接 ({comPort})");
return true;
}
catch (Exception ex)
{
Log?.Invoke($"[{OpName}] 扫码枪连接失败 ({comPort}){ex.Message}");
return false;
}
}
/// <summary>
/// 断开连接
/// </summary>
public void Disconnect()
{
try
{
_reconnectTimer?.Stop();
_flushTimer?.Stop();
if (_serial != null)
{
_serial.DataReceived -= Serial_DataReceived;
if (_serial.IsOpen) _serial.Close();
_serial.Dispose();
_serial = null;
}
}
catch { }
}
/// <summary>
/// 测试当前COM口是否可连接
/// </summary>
public bool TestConnection(string comPort)
{
try
{
using (var sp = new SerialPort(comPort, _baudRate))
{
sp.Open();
sp.Close();
return true;
}
}
catch { return false; }
}
// ── 串口数据接收 ──
private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
if (_serial == null || !_serial.IsOpen) return;
int bytesToRead = _serial.BytesToRead;
if (bytesToRead <= 0) return;
byte[] buf = new byte[bytesToRead];
_serial.Read(buf, 0, bytesToRead);
string chunk = Encoding.UTF8.GetString(buf, 0, bytesToRead);
lock (_bufferLock)
{
_buffer.Append(chunk);
string acc = _buffer.ToString();
// 按换行符(\r \n拆分完整条码
int startIndex = 0;
for (int i = 0; i < acc.Length; i++)
{
if (acc[i] == '\r' || acc[i] == '\n')
{
int len = i - startIndex;
if (len > 0)
{
string line = acc.Substring(startIndex, len).Trim();
if (!string.IsNullOrEmpty(line))
SubmitBarcode(line);
}
startIndex = i + 1;
}
}
if (startIndex >= acc.Length)
_buffer.Clear();
else if (startIndex > 0)
{
_buffer.Clear();
_buffer.Append(acc.Substring(startIndex));
}
// 无换行 → 启动空闲定时器
if (_buffer.Length > 0)
{
try
{
_flushTimer?.Stop();
_flushTimer?.Start();
}
catch { }
}
}
}
catch (Exception ex)
{
if (ex is System.IO.IOException || ex is InvalidOperationException)
{
Log?.Invoke($"[{OpName}] 扫码枪通信异常,尝试重连:{ex.Message}");
TryReopen();
}
}
finally
{
try { _serial?.DiscardInBuffer(); } catch { }
}
}
// 空闲超时提交缓冲区
private void FlushTimer_Tick(object sender, EventArgs e)
{
_flushTimer.Stop();
string toSend;
lock (_bufferLock)
{
toSend = _buffer.ToString().Trim();
_buffer.Clear();
}
if (!string.IsNullOrEmpty(toSend))
SubmitBarcode(toSend);
}
// 提交条码(含防抖)
private void SubmitBarcode(string barcode)
{
barcode = barcode.Replace("\r", "").Replace("\n", "").Replace("\0", "").Trim();
if (string.IsNullOrEmpty(barcode)) return;
// 防抖
var now = DateTime.Now;
if (barcode == _lastDebounceCode && (now - _lastDebounceTime).TotalMilliseconds < DebounceMs)
return;
_lastDebounceCode = barcode;
_lastDebounceTime = now;
// 更新状态
LastBarcode = barcode;
LastScanTime = now;
// 触发事件
BarcodeReceived?.Invoke(OpName, barcode);
}
/// <summary>
/// 手动发送条码(模拟扫码触发,走完整业务流程)
/// </summary>
public void ManualSend(string barcode)
{
if (!string.IsNullOrWhiteSpace(barcode))
SubmitBarcode(barcode.Trim());
}
// ── 断线重连 ──
private void ReconnectTimer_Tick(object sender, EventArgs e)
{
if (_reconnecting || string.IsNullOrWhiteSpace(ComPort)) return;
if (IsConnected) return;
_reconnecting = true;
Task.Run(() =>
{
try { TryReopen(); }
catch { }
finally { _reconnecting = false; }
});
}
private void TryReopen()
{
try
{
string[] ports = SerialPort.GetPortNames();
bool exists = Array.Exists(ports, p => string.Equals(p, ComPort, StringComparison.OrdinalIgnoreCase));
if (!exists) return;
if (_serial != null)
{
try { if (_serial.IsOpen) _serial.Close(); } catch { }
try { _serial.Dispose(); } catch { }
}
_serial = new SerialPort
{
PortName = ComPort,
BaudRate = _baudRate,
Parity = Parity.None,
StopBits = StopBits.One,
ReadTimeout = 1000,
DtrEnable = true
};
_serial.DataReceived += Serial_DataReceived;
_serial.Open();
Log?.Invoke($"[{OpName}] 扫码枪自动重连成功 ({ComPort})");
}
catch (Exception ex)
{
Log?.Invoke($"[{OpName}] 扫码枪自动重连失败 ({ComPort}){ex.Message}");
}
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
Disconnect();
_flushTimer?.Dispose();
_reconnectTimer?.Dispose();
}
}
}

40
SCADA/Core/IconHelper.cs Normal file
View File

@@ -0,0 +1,40 @@
using System;
using System.Drawing;
using System.Windows.Forms;
namespace MesWork
{
/// <summary>
/// 窗口图标加载工具类(多路径搜索 + exe回退
/// </summary>
public static class IconHelper
{
private static readonly string[] IconSearchPaths = new[]
{
System.IO.Path.Combine(Application.StartupPath, "任务管理.ico"),
System.IO.Path.Combine(System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location) ?? "", "任务管理.ico"),
"任务管理.ico"
};
/// <summary>
/// 为指定窗体加载图标
/// </summary>
public static void ApplyIcon(Form form)
{
try
{
foreach (var path in IconSearchPaths)
{
if (System.IO.File.Exists(path))
{
form.Icon = new Icon(path);
return;
}
}
form.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
}
catch { }
}
}
}

626
SCADA/Core/OExcel.cs Normal file
View File

@@ -0,0 +1,626 @@
using NPOI.HPSF;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NPOITest
{
/// <summary>
/// Execl工具辅助类
/// </summary>
public class ExeclHelper
{
/// <summary>
/// 读取Execl数据到DataTable中
/// </summary>
/// <param name="filePath">指定Execl文件路径</param>
/// <param name="isColumnName">设置第一行是否是列名</param>
/// <returns>返回一个DataTable数据集</returns>
public static DataTable ExcelToDataTable(string filePath, string sheetName, bool isColumnName)
{
DataTable dataTable = null;
FileStream fs = null;
DataColumn column = null;
DataRow dataRow = null;
IWorkbook workbook = null;
ISheet sheet = null;
IRow row = null;
ICell cell = null;
int startRow = 0;
try
{
using (fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
// 2007版本
if (filePath.IndexOf(".xlsx") > 0)
workbook = new XSSFWorkbook(fs);
// 2003版本
else if (filePath.IndexOf(".xls") > 0)
workbook = new HSSFWorkbook(fs);
if (workbook != null)
{
sheet = workbook.GetSheet(sheetName);//读取第一个sheet当然也可以循环读取每个sheet
dataTable = new DataTable();
if (sheet != null)
{
int rowCount = sheet.LastRowNum;//总行数
if (rowCount > 0)
{
IRow firstRow = sheet.GetRow(0);//第一行
int cellCount = firstRow.LastCellNum;//列数
//构建datatable的列
if (isColumnName)
{
startRow = 1;//如果第一行是列名,则从第二行开始读取
for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
{
cell = firstRow.GetCell(i);
if (cell != null)
{
if (cell.StringCellValue != null)
{
column = new DataColumn(cell.StringCellValue);
dataTable.Columns.Add(column);
}
}
}
}
else
{
for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
{
column = new DataColumn("column" + (i + 1));
dataTable.Columns.Add(column);
}
}
//填充行
for (int i = startRow; i <= rowCount; ++i)
{
row = sheet.GetRow(i);
if (row == null) continue;
dataRow = dataTable.NewRow();
for (int j = row.FirstCellNum; j < cellCount; ++j)
{
cell = row.GetCell(j);
if (cell == null)
{
dataRow[j] = "";
}
else
{
//CellType(Unknown = -1,Numeric = 0,String = 1,Formula = 2,Blank = 3,Boolean = 4,Error = 5,)
switch (cell.CellType)
{
case CellType.Blank:
dataRow[j] = "";
break;
case CellType.Numeric:
short format = cell.CellStyle.DataFormat;
//对时间格式2015.12.5、2015/12/5、2015-12-5等的处理
if (format == 14 || format == 31 || format == 57 || format == 58)
dataRow[j] = cell.DateCellValue;
else
dataRow[j] = cell.NumericCellValue;
break;
case CellType.String:
dataRow[j] = cell.StringCellValue;
break;
}
}
}
dataTable.Rows.Add(dataRow);
}
}
}
}
}
return dataTable;
}
catch (Exception err)
{
if (fs != null)
{
fs.Close();
}
return null;
}
}
public static DataTable ExcelToDataTable(string filePath,int sheetIndex, bool isColumnName)
{
DataTable dataTable = null;
FileStream fs = null;
DataColumn column = null;
DataRow dataRow = null;
IWorkbook workbook = null;
ISheet sheet = null;
IRow row = null;
ICell cell = null;
int startRow = 0;
try
{
using (fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
// 2007版本
if (filePath.IndexOf(".xlsx") > 0)
workbook = new XSSFWorkbook(fs);
// 2003版本
else if (filePath.IndexOf(".xls") > 0)
workbook = new HSSFWorkbook(fs);
if (workbook != null)
{
sheet = workbook.GetSheetAt(sheetIndex);//读取第一个sheet当然也可以循环读取每个sheet
dataTable = new DataTable();
if (sheet != null)
{
int rowCount = sheet.LastRowNum;//总行数
if (rowCount > 0)
{
IRow firstRow = sheet.GetRow(0);//第一行
int cellCount = firstRow.LastCellNum;//列数
//构建datatable的列
if (isColumnName)
{
startRow = 1;//如果第一行是列名,则从第二行开始读取
for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
{
cell = firstRow.GetCell(i);
if (cell != null)
{
if (cell.StringCellValue != null)
{
column = new DataColumn(cell.StringCellValue);
dataTable.Columns.Add(column);
}
}
}
}
else
{
for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
{
column = new DataColumn("column" + (i + 1));
dataTable.Columns.Add(column);
}
}
//填充行
for (int i = startRow; i <= rowCount; ++i)
{
row = sheet.GetRow(i);
if (row == null) continue;
dataRow = dataTable.NewRow();
for (int j = row.FirstCellNum; j < cellCount; ++j)
{
cell = row.GetCell(j);
if (cell == null)
{
dataRow[j] = "";
}
else
{
//CellType(Unknown = -1,Numeric = 0,String = 1,Formula = 2,Blank = 3,Boolean = 4,Error = 5,)
switch (cell.CellType)
{
case CellType.Blank:
dataRow[j] = "";
break;
case CellType.Numeric:
short format = cell.CellStyle.DataFormat;
//对时间格式2015.12.5、2015/12/5、2015-12-5等的处理
if (format == 14 || format == 31 || format == 57 || format == 58 || format == 22)
dataRow[j] = cell.DateCellValue;
else
dataRow[j] = cell.NumericCellValue;
break;
case CellType.String:
dataRow[j] = cell.StringCellValue;
break;
}
}
}
dataTable.Rows.Add(dataRow);
}
}
}
}
}
return dataTable;
}
catch (Exception)
{
if (fs != null)
{
fs.Close();
}
return null;
}
}
public static void DataTableToExcel(DataTable dataTable, string templatePath, string outputPath, int startRow, int startCol)
{
// 加载模板文件
using (FileStream fs = new FileStream(templatePath, FileMode.Open, FileAccess.Read))
{
IWorkbook workbook = new XSSFWorkbook(fs);
ISheet sheet = workbook.GetSheetAt(0); // 获取第一个工作表
// 创建单元格样式
ICellStyle cellStyle = workbook.CreateCellStyle();
IFont font = workbook.CreateFont();
font.FontHeightInPoints = 12;
cellStyle.SetFont(font);
cellStyle.BorderTop = BorderStyle.Thin;
cellStyle.BorderBottom = BorderStyle.Thin;
cellStyle.BorderLeft = BorderStyle.Thin;
cellStyle.BorderRight = BorderStyle.Thin;
cellStyle.Alignment = HorizontalAlignment.Center;
// 遍历DataTable的每一行
for (int i = 0; i < dataTable.Rows.Count; i++)
{
IRow row = sheet.CreateRow(startRow + i); // 创建新行
// 遍历DataTable的每一列
for (int j = 0; j < dataTable.Columns.Count; j++)
{
ICell cell = row.CreateCell(startCol + j); // 创建新单元格
cell.SetCellValue(dataTable.Rows[i][j].ToString());
cell.CellStyle = cellStyle; // 应用单元格样式
}
}
// 保存输出文件
using (FileStream outputStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write))
{
workbook.Write(outputStream);
}
}
}
/// <summary>
/// 将DataTable导出到Excel文档自动根据扩展名选择 .xlsx / .xls 格式)
/// </summary>
/// <param name="dt">传入一个DataTable数据集</param>
/// <param name="sheetName">工作表名称</param>
/// <param name="Outpath">导出文件完整路径(支持 .xlsx 和 .xls</param>
/// <returns>True表示导出成功False表示导出失败</returns>
public static bool DataTableToExcel(DataTable dt, string sheetName, string Outpath)
{
if (dt == null || dt.Rows.Count == 0 || string.IsNullOrWhiteSpace(Outpath))
return false;
IWorkbook workbook = null;
try
{
// ── 1. 根据文件扩展名选择正确的 Workbook 类型 ──
string ext = Path.GetExtension(Outpath).ToLowerInvariant();
if (ext == ".xlsx")
workbook = new XSSFWorkbook(); // OOXML 格式
else
workbook = new HSSFWorkbook(); // BIFF8 格式
ISheet sheet = workbook.CreateSheet(sheetName);
int rowCount = dt.Rows.Count;
int columnCount = dt.Columns.Count;
// ── 2. 创建表头样式(加粗 + 背景色 + 边框) ──
ICellStyle headerStyle = workbook.CreateCellStyle();
IFont headerFont = workbook.CreateFont();
headerFont.IsBold = true;
headerFont.FontHeightInPoints = 11;
headerFont.FontName = "微软雅黑";
headerStyle.SetFont(headerFont);
headerStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Grey25Percent.Index;
headerStyle.FillPattern = FillPattern.SolidForeground;
headerStyle.Alignment = HorizontalAlignment.Center;
headerStyle.VerticalAlignment = VerticalAlignment.Center;
headerStyle.BorderTop = BorderStyle.Thin;
headerStyle.BorderBottom = BorderStyle.Thin;
headerStyle.BorderLeft = BorderStyle.Thin;
headerStyle.BorderRight = BorderStyle.Thin;
// ── 3. 创建数据行样式(边框) ──
ICellStyle dataStyle = workbook.CreateCellStyle();
IFont dataFont = workbook.CreateFont();
dataFont.FontHeightInPoints = 10;
dataFont.FontName = "微软雅黑";
dataStyle.SetFont(dataFont);
dataStyle.VerticalAlignment = VerticalAlignment.Center;
dataStyle.BorderTop = BorderStyle.Thin;
dataStyle.BorderBottom = BorderStyle.Thin;
dataStyle.BorderLeft = BorderStyle.Thin;
dataStyle.BorderRight = BorderStyle.Thin;
// ── 4. 写入列头 ──
IRow headerRow = sheet.CreateRow(0);
headerRow.HeightInPoints = 22;
for (int c = 0; c < columnCount; c++)
{
ICell cell = headerRow.CreateCell(c);
cell.SetCellValue(dt.Columns[c].ColumnName);
cell.CellStyle = headerStyle;
}
// ── 5. 写入数据行 ──
for (int i = 0; i < rowCount; i++)
{
IRow row = sheet.CreateRow(i + 1);
for (int j = 0; j < columnCount; j++)
{
ICell cell = row.CreateCell(j);
object val = dt.Rows[i][j];
// 根据数据类型设置单元格值,保留数值精度
if (val == null || val == DBNull.Value)
{
cell.SetCellValue("");
}
else if (val is DateTime dtVal)
{
cell.SetCellValue(dtVal.ToString("yyyy-MM-dd HH:mm:ss"));
}
else if (val is double || val is float || val is decimal)
{
cell.SetCellValue(Convert.ToDouble(val));
}
else if (val is int || val is long || val is short)
{
cell.SetCellValue(Convert.ToDouble(val));
}
else
{
cell.SetCellValue(val.ToString());
}
cell.CellStyle = dataStyle;
}
}
// ── 6. 自动调整列宽(限制最大宽度避免过宽) ──
for (int c = 0; c < columnCount; c++)
{
sheet.AutoSizeColumn(c);
int colWidth = sheet.GetColumnWidth(c);
// 最大列宽限制为 50 个字符宽度 (50 * 256)
if (colWidth > 50 * 256)
sheet.SetColumnWidth(c, 50 * 256);
// 最小列宽
else if (colWidth < 10 * 256)
sheet.SetColumnWidth(c, 10 * 256);
}
// ── 7. 写入文件(使用 FileMode.Create 确保覆盖写入) ──
using (FileStream fs = new FileStream(Outpath, FileMode.Create, FileAccess.Write))
{
workbook.Write(fs);
}
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[ExeclHelper] 导出Excel失败: {ex.Message}");
return false;
}
finally
{
if (workbook != null)
{
workbook.Close();
workbook = null;
}
}
}
/// <summary>
/// 读取Execl数据到DataTable(DataSet)中
/// </summary>
/// <param name="filePath">指定Execl文件路径</param>
/// <param name="isFirstLineColumnName">设置第一行是否是列名</param>
/// <returns>返回一个DataTable数据集</returns>
public static DataSet ExcelToDataSet(string filePath, bool isFirstLineColumnName)
{
DataSet dataSet = new DataSet();
int startRow = 0;
try
{
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
IWorkbook workbook = null;
// 如果是2007+的Excel版本
if (filePath.IndexOf(".xlsx") > 0)
{
workbook = new XSSFWorkbook(fs);
}
// 如果是2003-的Excel版本
else if (filePath.IndexOf(".xls") > 0)
{
workbook = new HSSFWorkbook(fs);
}
if (workbook != null)
{
//循环读取Excel的每个sheet每个sheet页都转换为一个DataTable并放在DataSet中
for (int p = 0; p < workbook.NumberOfSheets; p++)
{
ISheet sheet = workbook.GetSheetAt(p);
DataTable dataTable = new DataTable();
dataTable.TableName = sheet.SheetName;
if (sheet != null)
{
int rowCount = sheet.LastRowNum;//获取总行数
if (rowCount > 0)
{
IRow firstRow = sheet.GetRow(0);//获取第一行
int cellCount = firstRow.LastCellNum;//获取总列数
//构建datatable的列
if (isFirstLineColumnName)
{
startRow = 1;//如果第一行是列名,则从第二行开始读取
for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
{
ICell cell = firstRow.GetCell(i);
if (cell != null)
{
if (cell.StringCellValue != null)
{
DataColumn column = new DataColumn(cell.StringCellValue);
dataTable.Columns.Add(column);
}
}
}
}
else
{
for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
{
DataColumn column = new DataColumn("column" + (i + 1));
dataTable.Columns.Add(column);
}
}
//填充行
for (int i = startRow; i <= rowCount; ++i)
{
IRow row = sheet.GetRow(i);
if (row == null) continue;
DataRow dataRow = dataTable.NewRow();
for (int j = row.FirstCellNum; j < cellCount; ++j)
{
ICell cell = row.GetCell(j);
if (cell == null)
{
dataRow[j] = "";
}
else
{
//CellType(Unknown = -1,Numeric = 0,String = 1,Formula = 2,Blank = 3,Boolean = 4,Error = 5,)
switch (cell.CellType)
{
case CellType.Blank:
dataRow[j] = "";
break;
case CellType.Numeric:
short format = cell.CellStyle.DataFormat;
//对时间格式2015.12.5、2015/12/5、2015-12-5等的处理
if (format == 14 || format == 31 || format == 57 || format == 58)
dataRow[j] = cell.DateCellValue;
else
dataRow[j] = cell.NumericCellValue;
break;
case CellType.String:
dataRow[j] = cell.StringCellValue;
break;
}
}
}
dataTable.Rows.Add(dataRow);
}
}
}
dataSet.Tables.Add(dataTable);
}
}
}
return dataSet;
}
catch (Exception err)
{
return null;
}
}
/// <summary>
/// 将DataTable(DataSet)导出到Execl文档
/// </summary>
/// <param name="dataSet">传入一个DataSet</param>
/// <param name="Outpath">导出路径(可以不加扩展名,不加默认为.xls</param>
/// <returns>返回一个Bool类型的值表示是否导出成功</returns>
/// True表示导出成功Flase表示导出失败
public static bool DataSetToExcel(DataSet dataSet, string Outpath)
{
bool result = false;
try
{
if (dataSet == null || dataSet.Tables == null || dataSet.Tables.Count == 0 || string.IsNullOrEmpty(Outpath))
throw new Exception("输入的DataSet或路径异常");
int sheetIndex = 0;
//根据输出路径的扩展名判断workbook的实例类型
IWorkbook workbook = null;
string pathExtensionName = Outpath.Trim().Substring(Outpath.Length - 5);
if (pathExtensionName.Contains(".xlsx"))
{
workbook = new XSSFWorkbook();
}
else if (pathExtensionName.Contains(".xls"))
{
workbook = new HSSFWorkbook();
}
else
{
Outpath = Outpath.Trim() + ".xls";
workbook = new HSSFWorkbook();
}
//将DataSet导出为Excel
foreach (DataTable dt in dataSet.Tables)
{
sheetIndex++;
if (dt != null && dt.Rows.Count > 0)
{
ISheet sheet = workbook.CreateSheet(string.IsNullOrEmpty(dt.TableName) ? ("sheet" + sheetIndex) : dt.TableName);//创建一个名称为Sheet0的表
int rowCount = dt.Rows.Count;//行数
int columnCount = dt.Columns.Count;//列数
//设置列头
IRow row = sheet.CreateRow(0);//excel第一行设为列头
for (int c = 0; c < columnCount; c++)
{
ICell cell = row.CreateCell(c);
cell.SetCellValue(dt.Columns[c].ColumnName);
}
//设置每行每列的单元格,
for (int i = 0; i < rowCount; i++)
{
row = sheet.CreateRow(i + 1);
for (int j = 0; j < columnCount; j++)
{
ICell cell = row.CreateCell(j);//excel第二行开始写入数据
cell.SetCellValue(dt.Rows[i][j].ToString());
}
}
}
}
//向outPath输出数据
using (FileStream fs = File.OpenWrite(Outpath))
{
workbook.Write(fs);//向打开的这个xls文件中写入数据
result = true;
}
return result;
}
catch (Exception ex)
{
return false;
}
}
}
}

25
SCADA/Core/PLC_R.cs Normal file
View File

@@ -0,0 +1,25 @@
using System;
using System.Text;
namespace DC_A95
{
public class PLC_R
{
/// <summary>
/// 清理PLC字符串乱码统一入口支持object参数
/// 过滤不可见字符保留可打印ASCII和中文等有效字符
/// </summary>
public static string GetString_CleanGarbled(object input)
{
if (input == null) return "";
string str = input.ToString();
if (string.IsNullOrEmpty(str)) return "";
var sb = new StringBuilder();
foreach (char c in str)
{
if (c >= 0x20 && c < 0xFFFE) sb.Append(c);
}
return sb.ToString().Trim();
}
}
}

View File

@@ -0,0 +1,72 @@
using MesWork;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
namespace MesWork
{
public class SqlOperation
{
public static bool ExecuteStoredProcedure(string name, SqlParameter[] sqlParameters, out string errorMessage)
{
return DataLinkMesWork.SQLCommon.ExecuteStoredProcedure(name, MesWorkForm.ConnectionString, ref sqlParameters, out errorMessage);
}
public static bool ExecuteStoredProcedure(string name, SqlParameter[] sqlParameters, out DataTable dt, out string errorMessage)
{
return DataLinkMesWork.SQLCommon.ExecuteStoredProcedure(name, MesWorkForm.ConnectionString, ref sqlParameters, out dt, out errorMessage);
}
public static bool ExecuteStoredProcedure(string name, SqlParameter[] sqlParameters, out DataSet ds, out string errorMessage)
{
return DataLinkMesWork.SQLCommon.ExecuteStoredProcedure(name, MesWorkForm.ConnectionString, ref sqlParameters, out ds, out errorMessage);
}
public static bool ExecuteSql(string sql, out DataTable dt, out string errorMessage)
{
return DataLinkMesWork.SQLCommon.ExecuteSql(sql, MesWorkForm.ConnectionString, out dt, out errorMessage);
}
public static bool ExecuteSql(string sql, out DataSet ds, out string errorMessage)
{
return DataLinkMesWork.SQLCommon.ExecuteDataset(sql, MesWorkForm.ConnectionString, out ds, out errorMessage);
}
public static bool ExecuteSql(string sql, out string errorMessage)
{
return DataLinkMesWork.SQLCommon.ExecuteSql(sql, MesWorkForm.ConnectionString, out errorMessage);
}
/// <summary>
/// 通过SqlBulkCopy复制table数据到数据库
/// </summary>
/// <param name="dataset"></param>
public static bool SqlbulkcopyInsert(DataTable dt, out string ErrMsg)
{
bool success = false;
ErrMsg = "";
try
{
// SqlBulkCopy sqlbulkcopy = new SqlBulkCopy(connectString, SqlBulkCopyOptions.KeepIdentity);//删除自增ID插入原始数据
SqlBulkCopy sqlbulkcopy = new SqlBulkCopy(MesWorkForm.ConnectionString, SqlBulkCopyOptions.UseInternalTransaction);//批量事务处理
sqlbulkcopy.DestinationTableName = dt.TableName;//数据库中的表名
for (int i = 0; i < dt.Columns.Count; i++)
{
var columnName = dt.Columns[i].ColumnName.ToString();
sqlbulkcopy.ColumnMappings.Add(columnName, columnName);
}
sqlbulkcopy.WriteToServer(dt);
success = true;
}
catch (Exception err)
{
ErrMsg = err.Message;
}
return success;
}
}
}