文章目录
一、实现功能
在Wincc项目中,一般可以使用用户归档功能进行参数配方的制作。虽然Wincc所提供的功能可以满足大部分的需求,但是依然存在着参数复制不便、配方参数不便于分组存储、用户归档结构更新导致旧归档数据丢失等问题。因此制作该应用通过另一种方法实现配方的功能:1.实时反应PLC当前运行配方;2.读取和保存的配方文件可以分组。
二、实现步骤
1.引入库
using CCHMIRUNTIME;//Wincc通讯
using unvell.ReoGrid.DataFormat;//表格控件
using System.Windows.Media.Animation;
using System.Windows.Threading;
using System.IO;
using System.Diagnostics;
using System.Security.Cryptography;
其中ReoGrid可以提供近似于Excel表格的功能:
2.定义公用变量,主程序初始化
public class STEPItem
{
public string C1 { get; set; }
public string C2 { get; set; }
public string C3 { get; set; }
public string C4 { get; set; }
public string C5 { get; set; }
public string C6 { get; set; }
}
public string PCindex;
public int User = 0;
private DispatcherTimer timer;
主程序如下:
public MainWindow()
{
InitializeComponent();
ExcelInit(PC1reogrid);
ExcelInit(PC2reogrid);//ReoGrid控件的外观初始化
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(6);
timer.Tick += seedListView;
//timer.Start();
//runstop.IsChecked = true;
//获取欲启动进程名
string strProcessName = System.Diagnostics.Process.GetCurrentProcess().ProcessName;
//检查进程是否已经启动,已经启动则显示报错信息退出程序。
if (System.Diagnostics.Process.GetProcessesByName(strProcessName).Length > 1)
{
MessageBox.Show("多个程序不能同时运行!", "警告");
try
{
System.Diagnostics.Process.GetCurrentProcess().Kill();
}
catch { }
return;
}
if (!Directory.Exists("D:\\配方程序\\PC1")) { Directory.CreateDirectory("D:\\配方程序\\PC1"); }
if (!Directory.Exists("D:\\配方程序\\PC2")) { Directory.CreateDirectory("D:\\配方程序\\PC2"); }//在系统中生成存放配方文件的文件夹
}
通过按钮触发定时器启动停止,并增加进度条动画:
private void runstop_Click(object sender, RoutedEventArgs e)
{
if (runstop.IsChecked == false)
{
//PGbar.IsIndeterminate = false;
timer.Stop();
PGbar.Value = 0;
}
else
{
//PGbar.IsIndeterminate = true;
timer.Start();
DoubleAnimation animation = new DoubleAnimation(
PGbar.Value = 0, // From
PGbar.Value = 100, // To
new Duration(TimeSpan.FromSeconds(6))) // Duration 间隔是 10s
{
AccelerationRatio = 0, // 设置加速
DecelerationRatio = 0, // 设置减速
};
PGbar.BeginAnimation(ProgressBar.ValueProperty, animation);
}
}
3.读取正在运行配方
通过使用CCHMIRUNTIME库读取正在运行的Wincc项目的变量。
代码如下:
private void seedListView(object sender, EventArgs e)
{
//Define
var data = new[]
{
new string[6],
new string[6],
new string[6],
new string[6],
new string[6],
};
//Read
runingrepview.Items.Clear();
Stopwatch ST = new Stopwatch();
ST.Start();
if (pc1.IsSelected)
{
PCindex = "PC1";
}
else
{
PCindex = "PC2";
}
int I;
CCHMIRUNTIME.HMIRuntime HMIRT = new CCHMIRUNTIME.HMIRuntime();
try
{
for (I = 1; I < 6; I++)
{
data[I - 1][0] = I.ToString();
data[I - 1][1] = HMIRT.Tags[PCindex + "工艺参数设定[" + "" + I + "" + "]_A"].Read();.ToString()
data[I - 1][2] = HMIRT.Tags[PCindex + "工艺参数设定[" + "" + I + "" + "]_B"].Read().ToString();
data[I - 1][3] = HMIRT.Tags[PCindex + "工艺参数设定[" + "" + I + "" + "]_C"].Read().ToString();
data[I - 1][4] = HMIRT.Tags[PCindex + "工艺参数设定[" + "" + I + "" + "]_D"].Read().ToString();
data[I - 1][5] = HMIRT.Tags[PCindex + "工艺参数设定[" + "" + I + "" + "]_E"].Read().ToString();
}
runningrepname.Text = HMIRT.Tags[PCindex + "工艺参数_配方名"].Read();
readtime.IsChecked = false;
}
catch (Exception)
{
timer.Stop();
runstop.IsChecked = false;
MessageBox.Show("读取失败,请检查WINCC是否运行");
return;
}
//Add
foreach (string[] version in data)
{
runingrepview.Items.Add(new STEPItem { C1 = version[0] , C2 = version[1] , C3 = version[2], C4 = version[3], C5 = version[4], C6 = version[5] });
}
ST.Stop();
readtime.Content = "读取时间:" + ST.Elapsed.TotalMilliseconds.ToString()+"ms";
ST.Reset();
DoubleAnimation animation = new DoubleAnimation(
PGbar.Value = 0, // From
PGbar.Value = 100, // To
new Duration(TimeSpan.FromSeconds(6))) // Duration 间隔是6s的动画
{
AccelerationRatio = 0, // 设置加速
DecelerationRatio = 0, // 设置减速
};
PGbar.BeginAnimation(ProgressBar.ValueProperty,animation);
}//读取正在运行配方
4.将读取到的配方上传至表格
读取的配方先转存在Listview控件里,点击按钮再将控件里的配方变量转移到ReoGrid表格控件里。
代码如下:
private void LoadSTEP_Click(object sender, RoutedEventArgs e)
{
unvell.ReoGrid.ReoGridControl worksheet;
if (pc1.IsSelected)
{
worksheet = PC1reogrid;
}
else
{
worksheet = PC2reogrid;
}
try
{
int I;
for (I = 1; I < 6; I++)
{
var version = runingrepview.Items[I - 1] as STEPItem;
worksheet.CurrentWorksheet[I + 1, 1] = version.C2;
worksheet.CurrentWorksheet[I + 1, 2] = version.C3;
worksheet.CurrentWorksheet[I + 1, 3] = version.C4;
worksheet.CurrentWorksheet[I + 1, 4] = version.C5;
worksheet.CurrentWorksheet[I + 1, 5] = version.C6;
}
worksheet.CurrentWorksheet["C1"] = runningrepname.Text;
}
catch (Exception)
{
MessageBox.Show("上传失败,请读取在线配方");
return;
}
}
5.将表格里的配方写入Wincc
代码如下:
private void WriteSTEP_Click(object sender, RoutedEventArgs e)
{
unvell.ReoGrid.ReoGridControl worksheet;
if (pc1.IsSelected)
{
worksheet = PC1reogrid;
}
else
{
worksheet = PC2reogrid;
}
worksheet.CurrentWorksheet.EndEdit(unvell.ReoGrid.EndEditReason.NormalFinish);//先取消选中单元格
writeAS(worksheet);
}
private void writeAS(unvell.ReoGrid.ReoGridControl Worksheet)
{
int I;
CCHMIRUNTIME.HMIRuntime HMIRT = new CCHMIRUNTIME.HMIRuntime();
Stopwatch ST = new Stopwatch();
ST.Start();
if (pc1.IsSelected)
{
PCindex = "PC1";
}
else
{
PCindex = "PC2";
}
try
{
for (I = 1; I < 6; I++)
{
HMIRT.Tags[PCindex + "工艺参数设定[" + "" + I + "" + "]_A"].Write(Worksheet.CurrentWorksheet[I + 1, 1]);
HMIRT.Tags[PCindex + "工艺参数设定[" + "" + I + "" + "]_B"].Write(Worksheet.CurrentWorksheet[I + 1, 2]);
HMIRT.Tags[PCindex + "工艺参数设定[" + "" + I + "" + "]_C"].Write(Worksheet.CurrentWorksheet[I + 1, 3]);
HMIRT.Tags[PCindex + "工艺参数设定[" + "" + I + "" + "]_D"].Write(Worksheet.CurrentWorksheet[I + 1, 4]);
HMIRT.Tags[PCindex + "工艺参数设定[" + "" + I + "" + "]_E"].Write(Worksheet.CurrentWorksheet[I + 1, 5]);
}
HMIRT.Tags[PCindex + "工艺参数_配方名"].Write(Worksheet.CurrentWorksheet["C1"]);
}
catch (Exception)
{
MessageBox.Show("写入失败");
return;
}
ST.Stop();
writetime.Content = "写入时间:" + ST.Elapsed.TotalMilliseconds.ToString()+"ms";
ST.Reset();
}//写入配方
6.保存编辑好的配方文件
文件的保存和读取加密参考了汐泽学园的方法,移步https://blog.youkuaiyun.com/yutiedun/article/details/105547508
private void savefilebutton_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.SaveFileDialog saveFileDialog = new Microsoft.Win32.SaveFileDialog();
saveFileDialog.Filter = "配方(*.rep)|*.rep";
unvell.ReoGrid.ReoGridControl worksheet;
if (pc1.IsSelected)
{
saveFileDialog.InitialDirectory = "D:\\配方程序\\PC1";
worksheet = PC1reogrid;
}
else
{
saveFileDialog.InitialDirectory = "D:\\配方程序\\PC2";
worksheet = PC2reogrid;
}
worksheet.CurrentWorksheet.EndEdit(unvell.ReoGrid.EndEditReason.NormalFinish);//先取消选中单元格
if (worksheet.CurrentWorksheet["C1"] != null)
{
saveFileDialog.FileName = worksheet.CurrentWorksheet["C1"].ToString();
if (saveFileDialog.ShowDialog() == true)
{
String filePath = saveFileDialog.FileName;
try
{
FileStream fs = new FileStream(filePath, FileMode.Create);
StreamWriter sw = new StreamWriter(fs);
int I, Y;
for (I = 0; I < 5; I++)
{
for (Y = 0; Y < 4; Y++)
{
sw.Write(AuthcodeHelper.Encode(worksheet.CurrentWorksheet.GetCellText(I + 2, Y + 1)) + " ");
}
sw.WriteLine();
}
sw.Write(worksheet.CurrentWorksheet["C1"]);
//清空缓冲区
sw.Flush();
//关闭流
sw.Close();
fs.Close();
}
catch (Exception)
{
MessageBox.Show("文件保存失败");
return;
}
}
}
else
{
MessageBox.Show("配方名不能为空");
}
}
7.读取保存的配方文件
private void openfilebutton_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.OpenFileDialog openFileDialog = new Microsoft.Win32.OpenFileDialog();
//过滤文件类型
openFileDialog.Filter = "配方(*.rep)|*.rep";
//单选
openFileDialog.Multiselect = false;
unvell.ReoGrid.ReoGridControl worksheet;
if (pc1.IsSelected)
{
openFileDialog.InitialDirectory = "D:\\配方程序\\PC1";
worksheet = PC1reogrid;
}
else
{
openFileDialog.InitialDirectory = "D:\\配方程序\\PC2";
worksheet = PC2reogrid;
}
if (log.Password == "123456")
{
if (openFileDialog.ShowDialog() == true)
{
String filePath = openFileDialog.FileName;
using (FileStream fs = new FileStream(filePath, FileMode.Open))
{
StreamReader rd = new StreamReader(fs);//读取文件中的数据
try
{
for (int I = 0; I < 5; I++) //读入数据并赋予数组
{
string line = rd.ReadLine();
string[] data = line.Split(' ');
for (int Y = 0; Y < 4; Y++)
{
worksheet.CurrentWorksheet[I + 2, Y + 1] = AuthcodeHelper.Decode(data[Y]);
}
}
worksheet.CurrentWorksheet["C1"] = rd.ReadLine();
}
catch (Exception)
{
MessageBox.Show("文件读取失败");
return;
}
rd.Close();
fs.Close();
}
}
}
}
8.ReoGrid表格控件的外观初始化
private void ExcelInit(unvell.ReoGrid.ReoGridControl Worksheet)
{
var worksheet = Worksheet.CurrentWorksheet;
worksheet.Rows = 7;
worksheet.Columns = 6;
worksheet.DisableSettings(unvell.ReoGrid.WorksheetSettings.Edit_DragSelectionToMoveCells);
worksheet.DisableSettings(unvell.ReoGrid.WorksheetSettings.View_ShowColumnHeader);
worksheet.DisableSettings(unvell.ReoGrid.WorksheetSettings.View_ShowRowHeader);//工作表初始化 行 列
worksheet.MergeRange("A1:B1");
worksheet.MergeRange("C1:E1");
worksheet.Ranges["A1:E1"].Style.TextColor = unvell.ReoGrid.Graphics.SolidColor.Blue;//合并单元格
worksheet["A1"] = "配方名称";
worksheet["A2:F2"] = new object[] { "NO", "A", "B", "C", "D", "E" };
worksheet.Ranges["A2:F2"].IsReadonly = true;//设置只读
worksheet["A3:A7"] = new object[] { 1, 2, 3, 4, 5};
worksheet.Ranges["A3:A7"].Style.TextColor = unvell.ReoGrid.Graphics.SolidColor.Black;
worksheet.Ranges["A2:F2"].IsReadonly = true;//设置只读
worksheet.Ranges["A1:F7"].Style.HorizontalAlign = unvell.ReoGrid.ReoGridHorAlign.Center;
worksheet.Ranges["A1:F1"].Style.BackColor = unvell.ReoGrid.Graphics.SolidColor.LightSteelBlue;
worksheet.Ranges["A2:F2"].Style.BackColor = unvell.ReoGrid.Graphics.SolidColor.LightSkyBlue;
worksheet.Ranges["A2:F2"].Style.TextColor = unvell.ReoGrid.Graphics.SolidColor.Black;
worksheet.Ranges["D3:F7"].Style.TextColor = unvell.ReoGrid.Graphics.SolidColor.Black;
worksheet.SetRangeBorders("A1:F7", unvell.ReoGrid.BorderPositions.InsideAll, new unvell.ReoGrid.RangeBorderStyle { Color = unvell.ReoGrid.Graphics.SolidColor.White, Style = unvell.ReoGrid.BorderLineStyle.Dashed });//网格线设置
worksheet.SetColumnsWidth(0, 12, 80);//列宽度设置
}
9.程序画面布局
程序的布局如下:
代码如下:
<Grid>
<Grid.RowDefinitions>
<RowDefinition ></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition ></RowDefinition>
</Grid.RowDefinitions>
<ProgressBar x:Name="PGbar" HorizontalAlignment="Left" Height="14" Margin="570,6,0,0" VerticalAlignment="Top" Width="150" Foreground="#FF41A800"/>
<Grid Grid.Row="0" VerticalAlignment="Stretch" >
<TabControl x:Name="PCcontrol" Margin="4,27,4,1">
<TabItem x:Name="pc1" Header="PC1" Margin="-2.2,0,-1.6,-0.2" RenderTransformOrigin="0.5,0.505">
<Grid Background="#FFE5E5E5">
<ReoGrid:ReoGridControl x:Name="PC1reogrid" SheetTabVisible="False" SnapsToDevicePixels="True" SheetTabNewButtonVisible="False" ShowScrollEndSpacing="False" SheetTabWidth="0" OverridesDefaultStyle="True"/>
</Grid>
</TabItem>
<TabItem x:Name="pc2" Header="PC2" Margin="-2.2,0,-1.6,-0.2" RenderTransformOrigin="0.5,0.505">
<Grid Background="#FFE5E5E5">
<ReoGrid:ReoGridControl x:Name="PC2reogrid" SheetTabVisible="False" SnapsToDevicePixels="True" SheetTabNewButtonVisible="False" ShowScrollEndSpacing="False" SheetTabWidth="0" OverridesDefaultStyle="True"/>
</Grid>
</TabItem>
</TabControl>
</Grid>
<GridSplitter Grid.Row="1" Height="5" VerticalAlignment="Center" HorizontalAlignment="Stretch"/>
<Grid Grid.Row="2" VerticalAlignment="Stretch" >
<Expander x:Name="runingrep" Header="运行配方名称" Margin="3,0,2.6,2" Cursor="" IsExpanded="True" ForceCursor="True">
<Grid Background="#FFE5E5E5" Margin="0">
<ListView x:Name="runingrepview" Margin="0,2,0,0">
<ListView.View>
<GridView>
<GridViewColumn Header="配方监控" Width="80" DisplayMemberBinding="{Binding C1}"/>
<GridViewColumn Header="A" Width="80" DisplayMemberBinding="{Binding C2}"/>
<GridViewColumn Header="B" Width="80" DisplayMemberBinding="{Binding C3}"/>
<GridViewColumn Header="C" Width="80" DisplayMemberBinding="{Binding C4}"/>
<GridViewColumn Header="D" Width="80" DisplayMemberBinding="{Binding C5}"/>
<GridViewColumn Header="E" Width="80" DisplayMemberBinding="{Binding C6}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Expander>
<TextBox x:Name="runningrepname" HorizontalAlignment="Left" Height="20" Margin="135,2,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="82" Background="{x:Null}" SelectionBrush="{x:Null}" TextAlignment="Center" FontWeight="Bold" BorderBrush="{x:Null}" IsEnabled="False"/>
</Grid>
<PasswordBox x:Name="log" HorizontalAlignment="Left" Margin="4,4,0,0" VerticalAlignment="Top" Width="72" Height="18"/>
<Button x:Name="Load" Content="登录" HorizontalAlignment="Left" Height="18" Margin="81,4,0,0" VerticalAlignment="Top" Width="49" Click="Load_Click"/>
<Button x:Name="LoadSTEP" Content="上传配方" HorizontalAlignment="Left" Height="18" Margin="135,4,0,0" VerticalAlignment="Top" Width="60" Click="LoadSTEP_Click" IsEnabled="False"/>
<Button x:Name="WriteSTEP" Content="下载配方" HorizontalAlignment="Left" Height="18" Margin="200,4,0,0" VerticalAlignment="Top" Width="60" Click="WriteSTEP_Click" IsEnabled="False"/>
<Button x:Name="openfilebutton" Content="打开配方文件" HorizontalAlignment="Left" Height="18" Margin="265,4,0,0" VerticalAlignment="Top" Width="100" Click="openfilebutton_Click" IsEnabled="False"/>
<Button x:Name="savefilebutton" Content="保存配方文件" HorizontalAlignment="Left" Height="18" Margin="370,4,0,0" VerticalAlignment="Top" Width="100" Click="savefilebutton_Click" IsEnabled="False"/>
<CheckBox x:Name="runstop" Content="读取在线配方" HorizontalAlignment="Left" Height="17" Margin="475,6,0,0" VerticalAlignment="Top" Click="runstop_Click" IsEnabled="False"/>
<RadioButton x:Name="readtime" Content="" HorizontalAlignment="Left" Height="18" Margin="570,6,0,0" VerticalAlignment="Top" Background="{x:Null}"/>
<RadioButton x:Name="writetime" Content="" HorizontalAlignment="Left" Height="18" Margin="720,6,5,0" VerticalAlignment="Top"/>
</Grid>
运行测试
在Wincc的变量管理中新增我们需要的内部变量。(跟PLC通讯时可以直接使用读取的AS符号变量)
点击激活Wincc项目,再运行配方程序,点击“读取在线配方”
可以看到Wincc中的变量已经被读取上传了
再点击“上传配方”按钮,看到当前运行的配方被上传至表格中,可以开始对配方进行编辑了。
点击“下载配方”按钮,可以看到修改后的配方已经上传到Wincc了。