zoukankan      html  css  js  c++  java
  • 基于西门子PLC和触摸屏以及C#开发的通用IO监控系统

    前言: 在项目开发的过程之中,经常需要做IO监控画面.当IO监控点多的时候,往往需要做很多的画面,并且浪费了HMI的很多IO点.做起画面来也很麻烦和繁琐.从而,让我思考着如何做一个工具.可以在一个画面上监控所有的变量.

    要实现这个,其实也不难.

    1,需要将IO变量泛型的通过指针的方式,反馈到一个变量上,并且使用这个变量的位来指示每个IO的状态.

    2,需要将IO的文本解释生成一系列的IO图片,然后在HMI上建立IO列表.关联起来.

    一,使用C#开发图片生成工具:

    image

    在左侧输入,I的文本解析,在右侧输入O的文本解析.在IO地址处放入起始地址.然后点击生成图片,就会生成一系列的图片了.

    image

    300表示从I300.0和Q300.0开始的IO变量.点击生成图片,开始生成:

    image

    打开一张图片,看下效果:名字叫 IO_0.PNG

    image

    最后一张 名字叫 IO_15.PNG

    image

    建立完之后,把所有图片拷贝项目文件夹里面,并且建立图形列表,名字叫IOtable

    image

    再建立一个IO点灭和亮通过不同形式表示的图形列表,IO显示(反正大家可以自己去设)

    image

    做完之后,再新建一个画面

    image

    这是仿真的,实际画面里面是 一个图形IO,绑定,刚才建立的IOTable图形列表.以及32个图形IO,绑定刚才建立的IO显示图形列表

    外加两个翻页按钮,当按左边时候,循环向下翻页,按右边时循环向上翻页(翻到底时就重复到起始地址).

    这是翻到第一页.可以看到,第一个I300.0没有被点亮,而第二页,则被点亮了.

    image

    实际上是连接了一个变量:

    image

    二,我们在PLC里面来实现这个功能:新建一个FB块,名字叫IO_Monitor

    image

    输入接口说明:

    •                Random:如果为true,则表示是无序IO,需要人工设定IOTable表,以绑定每个Index对应的IO地址.

                                如果为False,则自动建立IO地址和Index之间的关系.默认是自动建立:

    IF NOT #Random AND "FirstScan" THEN
        FOR #i := 0 TO #LastIndex DO
            #IOTable[#i] := #StartAddr + #i * 2;//因为地址是字节为单位,Index是以字为单位.IOTable[i],表示当前索引对应的IO地址.
        END_FOR; //StartAddr ---还记得吗?就是上面的开始地址.也就是支持偏置.
    END_IF;
    •              LastIndex:表明最终索引号,本列是15.表示有0-15,16个图片,共16*16,200多I点,和200多O点
    •              StartAddr:已经说明.根据实际IO地址填入.
    •              IOInterface.Left_Sw: 左翻页,实际就是把Index-1
    •              IOInterface.Right_Sw:右翻页,实际就是把Index+1
    •             为防止Index<0或>LastIndex,我做了个处理,当其为0-1时则其=最大值,反之LastIndex+1时,为最小值.    
    •             IOStatus,为当前索引的IO状态.
    •             IOIndex为当前索引.

    实现:


    image

    通过 除以2 来找到在Word数组中的位置. 通过Move_BLK_VAriant函数,来将本来不序列化的Any类型转为了一个WOrd数组.

    也就是 P#I0.0 Word 1000,实际上变成了一个  数组 tmp[0..999] of Word -----其中,tmp[0]=IW0,tmp[1]=IW2,tmp[999]=Iw1998.---------从而,也理解了刚才除以2的意义.因为,当我寻址I300时候,实际上在数组中,是tmp[150].

    这是翻页的实现:




    image

    这是CircleMode,其用处是将任意值取模到指定范围内.比如 –1 ,取模后变成了15.16,取模后变成了0.

    image

    二,下面讲一下利用C#实现自动生成图片的工具制作方法.

          1,首先建立操作界面:

    <Window x:Class="EsayTools.IoTable"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:EsayTools"
            mc:Ignorable="d"
            Title="IO图片生成器" Height="493.31" Width="1293.16" FontSize="20" WindowStyle="ToolWindow">
        <Grid >
            <Grid.RowDefinitions>
                <RowDefinition Height="40"></RowDefinition>
                <RowDefinition Height="*"></RowDefinition>
                <RowDefinition Height="40"></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <TextBlock Text="输入点文本" VerticalAlignment="Center" Margin="20 0 0 0" FontSize="20" FontStyle="Italic" Foreground="Red"></TextBlock>
            <TextBlock Grid.Column="1" Text="输出点文本" VerticalAlignment="Center" Margin="20 0 0 0" FontSize="20" FontStyle="Italic" Foreground="Red"></TextBlock>
            <TextBox x:Name="Txt1" Grid.Row="1" TextWrapping="Wrap" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" Background="AliceBlue" BorderThickness="3" BorderBrush="Black" Margin="2"></TextBox>
            <TextBox x:Name="Txt2" Grid.Row="1" Grid.Column="1" TextWrapping="Wrap" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" Background="AliceBlue" BorderThickness="3" BorderBrush="Black" Margin="2"></TextBox>
            <TextBlock Text="IO起始地址:" Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="20 0 20 0" FontSize="20" FontStyle="Italic" Foreground="Red"></TextBlock>
            <Grid  Grid.Column="3" Grid.Row="3">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition></ColumnDefinition>
                    <ColumnDefinition></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <TextBox x:Name="IOAddr" BorderBrush="Black" BorderThickness="3" Background="AliceBlue" ></TextBox>
                <Button  Click="bt1_Click" Grid.Column="2" Margin="10 2 10 2" FontSize="20">生成图片</Button>
    
            </Grid>
        </Grid>
    </Window>

    结果:

    image

    其次:建立界面效果图:

    image

    由于在一个TextBlock中同时显示IO,存在一个设计数据对齐的问题.也就是说,比如一个汉字对应2个字母,对应3个空格等等.

    最后解决办法,将字体敢为SimSun---凭借我不怎么好的英语瞎拆下是不是"仿宋"的意思??

    对齐的算法:

     private string FormatStringToChina(string str, int len)
            {
                return str + new string(' ', len - Encoding.GetEncoding("gb2312").GetBytes(str).Length);
    
            }

    len:为要对齐的最大长度.(在这里,有些字体是不行的...)

    首先将写入操作界面的字符串转为字符串数组:

     string[] strIs = ParentTxt1.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
                string[] strOs = ParentTxt2.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
                var Len = ((strIs.Length+15) / 16)> ((strOs.Length + 15 )/ 16)?((strIs.Length + 15 )/ 16): ((strOs.Length + 15 )/ 16);
                int StartAddr = ParentAddr; ;
                //检查文本框
                if (strIs.Length == 0 && strOs.Length == 0)
                {
                    System.Windows.Forms.MessageBox.Show("请填入正确的数据,IO文本数量不匹配");
                    return;
                }
    
                if(ParentOutputFile==null)
                {
    
                        System.Windows.Forms.MessageBox.Show("未输入正确文件名");
                        return;
    
    
                }

    其次,生成IO字符串数组

     private void generateStrings(int addr, out string[] iaddrs, out string[] oaddrs)
            {
                iaddrs = new String[16];
                oaddrs = new string[16];
    
                for (int i = 0; i < 16; i++)
                {
                    iaddrs[i] = "I" + (addr + i / 8) + "." + (i % 8);
                    oaddrs[i] = "Q" + (addr + i / 8) + "." + (i % 8);
                }
            }

    然后迭代  刷新TextBlock并且生成图片:

    刷新TextBlock代码:

    public void GenerateItem(TextBlock textBlock, string Iaddr, string Itxt, string Oaddr, string Otxt)
            {
                if(Itxt==null || Itxt==String.Empty)
                {
                    Itxt ="备用";
                }
                if (Otxt == null || Itxt == String.Empty)
                {
                    Otxt = "备用";
                }
    
                textBlock.Text = "     " + FormatStringToChina(Iaddr, 10) + FormatStringToChina(Itxt, 40) + FormatStringToChina(Oaddr, 10) + FormatStringToChina(Otxt, 40);
                if(IsBlue)
                {
                    textBlock.Background = Brushes.Orange;
                    IsBlue = false;
                }
                else
    
                {
                    textBlock.Background = Brushes.Brown;
                    IsBlue = true;
                }
    
            }
    
            public void GenerateItems(String[] Inops, String[] Onops, string[] Itxts, string[] Otxts)
            {
                int Index = 0;
                foreach (var Item in grid1.Children)
                {
                    TextBlock textBlock = (TextBlock)Item;
                    GenerateItem(textBlock, Inops[Index], Itxts[Index], Onops[Index], Otxts[Index]);
                    Index++;
                }
            }

    生成图片代码(网上找的):用于将Visual控件的内容转为图片,我这边是grid1,也就是包含了16个block的一个布局面板.

     private void GetPicFromControl(FrameworkElement element, String type, String outputPath)
            {
                //96为显示器DPI
                var bitmapRender = new RenderTargetBitmap((int)element.ActualWidth, (int)element.ActualHeight + 100, 96, 96, PixelFormats.Pbgra32);//位图 宽度  高度   水平DPI  垂直DPI  位图的格式    高度+100保证整个图都能截取
                //控件内容渲染RenderTargetBitmap
                bitmapRender.Render(element);
                BitmapEncoder encoder = null;
                //选取编码器
                switch (type.ToUpper())
                {
                    case "BMP":
                        encoder = new BmpBitmapEncoder();
                        break;
                    case "GIF":
                        encoder = new GifBitmapEncoder();
                        break;
                    case "JPEG":
                        encoder = new JpegBitmapEncoder();
                        break;
                    case "PNG":
                        encoder = new PngBitmapEncoder();
                        break;
                    case "TIFF":
                        encoder = new TiffBitmapEncoder();
                        break;
                    default:
                        break;
                }
                //对于一般的图片,只有一帧,动态图片是有多帧的。
                encoder.Frames.Add(BitmapFrame.Create(bitmapRender));//添加图
                if (!Directory.Exists(System.IO.Path.GetDirectoryName(outputPath)))
                    Directory.CreateDirectory(System.IO.Path.GetDirectoryName(outputPath));
                using (var file = File.Create(outputPath))//存储文件
                    encoder.Save(file);
    
    
    
            }


    这里的一个关键问题在于如何循环生成多张图片.

       private void GenerateOneBitMap(IOTableForClip Clip, int StartAddr, int CurAddr, string[] Itxtss, string[] Qtxtss)
            {
                string[] Itxts = new string[16];
                string[] Qtxts = new string[16];
                for (int i = 0; i < 16; i++)
                {
                    var Offset = (CurAddr - StartAddr) * 8;
                    if (Itxtss.Length >= Offset + i + 1)
                    {
                        Itxts[i] = Itxtss[Offset + i];
                    }
                    if (Qtxtss.Length >= Offset + i + 1)
                    {
                        Qtxts[i] = Qtxtss[Offset + i];
                    }
    
    
                }
    
                generateStrings(CurAddr, out string[] Iaddrs, out string[] Oaddrs);
                GenerateItems(Iaddrs, Oaddrs, Itxts, Qtxts);
                String[] PathName = ParentOutputFile.Split('.');
                this.UpdateLayout();
                GetPicFromControl(grid1, "PNG", PathName[0]+"_"+((CurAddr-StartAddr)/2)+".PNG");
    
            }

    UpdateLayout();函数非常的关建,只有刷新后,才能正确的显示实际的图片.

    附录,git地值https://gitee.com/mao_qin_bin/easy-tools.git.

  • 相关阅读:
    hdu 3666 差分约束系统
    hdu 1198农田灌溉
    常微分方程(阿諾爾德) Page 45 相空間,相流,運動,相曲線 註記
    高等微積分(高木貞治) 1.4節 例2
    常微分方程(阿諾爾德) Page 45 相空間,相流,運動,相曲線 註記
    解析函數論 Page 29 命題(2) 函數模的有界性
    高等微積分(高木貞治) 1.4節 例2
    解析函數論 Page 29 命題(1) 有界閉集上的一致連續性
    解析函數論 Page 29 命題(3) 模的下界的可達性
    解析函數論 Page 29 命題(2) 函數模的有界性
  • 原文地址:https://www.cnblogs.com/frogkiller/p/14206073.html
Copyright © 2011-2022 走看看