Пример #1
0
        /// <summary>
        /// 内部生成PLC指令的方法
        /// </summary>
        /// <param name="insts">生成的PLC指令的列表</param>
        /// <param name="expr">表达式</param>
        /// <param name="start">表达式的开始位置</param>
        /// <param name="end">表达式的结束位置</param>
        /// <param name="flag">标记</param>
        static private void _GenInst(List <PLCInstruction> insts, string expr, int start, int end, int flag = 0)
        {
            if (start > end)
            {
                return;
            }
            //Console.WriteLine(expr.Substring(start, end - start + 1));
            int bracket = 0;
            // 当前单元的结尾
            int uend = end;

            // CASE 1:查询并处理表达式中的或运算符(优先级最高)
            if ((flag & FLAG_HASOR) == 0)
            {
                bracket = 0;
                while (uend >= start && (bracket > 0 || expr[uend] != '|'))
                {
                    if (expr[uend] == '(')
                    {
                        bracket--;
                    }
                    if (expr[uend] == ')')
                    {
                        bracket++;
                    }
                    uend--;
                }
                if (uend >= start && expr[uend] == '|')
                {
                    // 如果前面需要与合并,可以拆掉括号逐一运算
                    if ((flag & FLAG_CALOR) != 0)
                    {
                        _GenInst(insts, expr, start, uend - 2, FLAG_CALOR);
                        _GenInst(insts, expr, uend + 1, end, FLAG_HASOR | FLAG_CALOR);
                    }
                    else
                    {
                        // 先计算或运算符前的部分
                        _GenInst(insts, expr, start, uend - 2);
                        // 将或运算符前的部分和后面进行或运算
                        _GenInst(insts, expr, uend + 1, end, FLAG_HASOR | FLAG_CALOR);
                        // 需要和前面与运算时
                        if ((flag & FLAG_CALAND) != 0)
                        {
                            InstHelper.AddInst(insts, "ANDB");
                        }
                    }
                    return;
                }
            }
            // CASE 2:查询并处理表达式中的与运算符(优先级其次)
            if ((flag & FLAG_HASAND) == 0)
            {
                bracket = 0;
                uend    = end;
                while (uend >= start && (bracket > 0 || expr[uend] != '&'))
                {
                    if (expr[uend] == '(')
                    {
                        bracket--;
                    }
                    if (expr[uend] == ')')
                    {
                        bracket++;
                    }
                    uend--;
                }
                if (uend >= start && expr[uend] == '&')
                {
                    // 如果前面需要与合并,可以拆掉括号逐一运算
                    if ((flag & FLAG_CALAND) != 0)
                    {
                        _GenInst(insts, expr, start, uend - 2, FLAG_HASOR | FLAG_CALAND);
                        _GenInst(insts, expr, uend + 1, end, FLAG_HASOR | FLAG_HASAND | FLAG_CALAND);
                    }
                    else
                    {
                        // 先计算与运算符前的部分
                        _GenInst(insts, expr, start, uend - 2, FLAG_HASOR);
                        // 将与运算符前的部分和后面进行与运算
                        _GenInst(insts, expr, uend + 1, end, FLAG_HASOR | FLAG_HASAND | FLAG_CALAND);
                        // 需要和前面或运算时
                        if ((flag & FLAG_CALOR) != 0)
                        {
                            InstHelper.AddInst(insts, "ORB");
                        }
                    }
                    return;
                }
            }
            // CASE 3:当前为单一单元
            // 当前表达式被括号包围时拆掉括号
            if (expr[start] == '(' && expr[end] == ')')
            {
                _GenInst(insts, expr, start + 1, end - 1);
                // 前面要或运算,所以要用栈的或合并
                if ((flag & FLAG_CALOR) != 0)
                {
                    InstHelper.AddInst(insts, "ORB");
                }
                // 前面要与运算,所以要用栈的与合并
                if ((flag & FLAG_CALAND) != 0)
                {
                    InstHelper.AddInst(insts, "ANDB");
                }
                return;
            }
            string profix = "LD";

            // 前面要或运算,所以用或前缀
            if ((flag & FLAG_CALOR) != 0)
            {
                profix = "OR";
            }
            // 前面要与运算,所以用与前缀
            if ((flag & FLAG_CALAND) != 0)
            {
                profix = "AND";
            }
            // 对表达式进行解析
            // 识别开始的元件ID标记
            int idend = start + 1;

            while (expr[idend] != ']')
            {
                idend++;
            }
            int id = int.Parse(expr.Substring(start + 1, idend - start - 1));

            start = idend + 1;
            // 识别开始的非符号
            if (expr[start] == '!')
            {
                // 如果是取反运算( ! )
                if (end == start)
                {
                    InstHelper.AddInst(insts, "INV", id);
                    return;
                }
                // 识别非符号后面的立即符号( !imM0 )
                if (expr[start + 1] == 'i' && expr[start + 2] == 'm')
                {
                    InstHelper.AddInst(insts, profix + "IIM " + expr.Substring(start + 3, end - start - 2), id);
                    return;
                }
                // 一般的非符号( !M0 )
                InstHelper.AddInst(insts, profix + "I " + expr.Substring(start + 1, end - start), id);
                return;
            }
            // 识别上升沿符号(ueM0)
            if (expr[start] == 'u' && expr[start + 1] == 'e')
            {
                // 如果是取上升沿运算( ue )
                if (end == start + 1)
                {
                    InstHelper.AddInst(insts, "MEP", id);
                    return;
                }
                InstHelper.AddInst(insts, profix + "P " + expr.Substring(start + 2, end - start - 1), id);
                return;
            }
            // 识别下降沿符号(deM0)
            if (expr[start] == 'd' && expr[start + 1] == 'e')
            {
                // 如果是取下降沿运算( de )
                if (end == start + 1)
                {
                    InstHelper.AddInst(insts, "MEF", id);
                    return;
                }
                InstHelper.AddInst(insts, profix + "F " + expr.Substring(start + 2, end - start - 1), id);
                return;
            }
            // 识别立即符号(imM0)
            if (expr[start] == 'i' && expr[start + 1] == 'm')
            {
                InstHelper.AddInst(insts, profix + "IM " + expr.Substring(start + 2, end - start - 1), id);
                return;
            }
            // 比较表达式的长度都不小于6
            if (end - start > 4)
            {
                if (profix.Equals("AND"))
                {
                    profix = "A";
                }
                // 找到比较符的位置
                int op = start + 2;
                while (expr[op] != '=' && expr[op] != '<' && expr[op] != '>')
                {
                    op++;
                }
                // 识别比较符前的数据类型
                switch (expr[op - 1])
                {
                case 'w': profix += 'W'; break;

                case 'd': profix += 'D'; break;

                case 'f': profix += 'F'; break;
                }
                // 等比较(M0w=M1)
                if (expr[op] == '=')
                {
                    InstHelper.AddInst(insts, profix + "EQ " + expr.Substring(start, op - 1 - start) + " " + expr.Substring(op + 1, end - op), id);
                    return;
                }
                // 不等比较(M0w<>M1)
                if (expr[op] == '<' && expr[op + 1] == '>')
                {
                    InstHelper.AddInst(insts, profix + "NE " + expr.Substring(start, op - 1 - start) + " " + expr.Substring(op + 2, end - op - 1), id);
                    return;
                }
                // 小等比较(M0w<=M1)
                if (expr[op] == '<' && expr[op + 1] == '=')
                {
                    InstHelper.AddInst(insts, profix + "LE " + expr.Substring(start, op - 1 - start) + " " + expr.Substring(op + 2, end - op - 1), id);
                    return;
                }
                // 大等比较(M0w>=M1)
                if (expr[op] == '>' && expr[op + 1] == '=')
                {
                    InstHelper.AddInst(insts, profix + "GE " + expr.Substring(start, op - 1 - start) + " " + expr.Substring(op + 2, end - op - 1), id);
                    return;
                }
                // 小于比较(M0w<M1)
                if (expr[op] == '<')
                {
                    InstHelper.AddInst(insts, profix + "L " + expr.Substring(start, op - 1 - start) + " " + expr.Substring(op + 1, end - op), id);
                    return;
                }
                // 大于比较(M0w>M1)
                if (expr[op] == '>')
                {
                    InstHelper.AddInst(insts, profix + "G " + expr.Substring(start, op - 1 - start) + " " + expr.Substring(op + 1, end - op), id);
                    return;
                }
            }
            // 单一的位寄存器(M0)
            string _expr = expr.Substring(start, end - start + 1);

            if (_expr.Equals("1"))
            {
                return;
            }
            InstHelper.AddInst(insts, profix + " " + _expr, id);
        }
Пример #2
0
        /// <summary>
        /// 通过区间内给定的逻辑图终点来生成PLC指令
        /// </summary>
        /// <param name="terminates">终点列表</param>
        /// <param name="left">区间头</param>
        /// <param name="right">区间尾</param>
        /// <param name="padding">访问过的最大前缀</param>
        /// <param name="flag">标记</param>
        /// <detail>
        /// 这↑里↓和表达式合并的想法一样,所以也需要将表达式排序
        /// 不同的是,这里要解决的是辅助栈的分配问题,所以需要找到所有分歧点
        /// 然后根据数量来确定辅助栈的操作
        /// </detail>
        static private void _GenInst(List <PLCInstruction> insts, List <Terminate> terminates, int left, int right, int padding = 0, int flag = 0)
        {
            int i, j;

            // 只有一个终点时,需要对这个终点单元进行解析
            // 一般来讲,这个单元的表达式被里面的坐标集合分为多段
            // 需要对每一段分别进行计算和执行终点指令
            if (left == right)
            {
                Terminate term = terminates[left];
                // 生成第一段
                _GenInst(insts, term.Expr, padding, term.TPoints[0].ExprIndex, flag);
                if (term.TPoints[0].enable)
                {
                    term.TPoints[0].PLCUnit.GenInst(insts, flag);
                }
                for (i = 1; i < term.TPoints.Count; i++)
                {
                    // 依次生成每一段
                    if (term.TPoints[i - 1].ExprIndex + 2 < term.TPoints[i].ExprIndex)
                    {
                        _GenInst(insts, term.Expr, term.TPoints[i - 1].ExprIndex + 3, term.TPoints[i].ExprIndex, flag | FLAG_CALAND);
                    }
                    if (term.TPoints[i].enable)
                    {
                        term.TPoints[i].PLCUnit.GenInst(insts, flag);
                    }
                }
            }
            else
            {
                // 扩展公共前缀
                bool allequal = true;                      // 判断所有表达式在当前位是否相等
                //bool allinside = true;               // 判断当前位是否至少被一个表达式包含
                List <int> difpoints   = new List <int>(); // 分歧点队列
                int        old_padding = padding;
                int        andpos      = padding;          // 这里需要记录最后的与运算符的位置

                /*
                 * 开始查找下一个分歧点
                 * 注意当前检查的位置超过其中一个表达式的长度时,两个表达式比较时视为相等
                 */
                while (allequal)
                {
                    for (i = left + 1; i <= right; i++)
                    {
                        string expr1 = terminates[i - 1].Expr;
                        string expr2 = terminates[i].Expr;
                        if (expr1[padding] != expr2[padding])
                        {
                            allequal = false;
                            break;
                        }
                    }
                    if (allequal == true)
                    {
                        padding++;
                    }
                }
                // 向前修正前缀末尾为and计算符
                while (padding > old_padding && terminates[left].Expr[padding] != '&')
                {
                    padding--;
                }
                // 然后向后查找所有的分歧点
                for (i = left + 1; i <= right; i++)
                {
                    string expr1 = terminates[i - 1].Expr;
                    string expr2 = terminates[i].Expr;

                    /*
                     * 两个表达式都到达末尾的情况下
                     * 由于表达式的末尾都是原型不同的常量1,所以无需比较默认相同
                     */
                    bool isend1 = !expr1.Substring(padding + 1).Contains('&');
                    bool isend2 = !expr2.Substring(padding + 1).Contains('&');
                    if (isend1 && isend2)
                    {
                        continue;
                    }
                    // 向后查找直到到达and运算符为止
                    for (j = padding + 1; j < expr1.Length && j < expr2.Length; j++)
                    {
                        if (!expr1[j].Equals(expr2[j]))
                        {
                            break;
                        }
                        if (expr1[j] == '&')
                        {
                            break;
                        }
                    }

                    /* 若不相同则为分歧点
                     * 当下标超过某个表达式的长度时,不视为分歧点
                     */
                    if (j < expr1.Length && j < expr2.Length && !expr1[j].Equals(expr2[j]))
                    {
                        difpoints.Add(i);
                    }
                }
                // CASE 1 : 若未找到新的公共前缀
                if (old_padding >= padding)
                {
                    // 如果不存在分歧点,直接输出所有结果指令
                    if (difpoints.Count() == 0)
                    {
                        for (i = left; i <= right; i++)
                        {
                            _GenInst(insts, terminates, i, i, padding, flag);
                        }
                    }
                    else
                    {
                        // 根据分歧点将所有终点进行划分,然后分别处理
                        _GenInst(insts, terminates, left, difpoints[0] - 1, padding, flag);
                        for (i = 1; i < difpoints.Count; i++)
                        {
                            _GenInst(insts, terminates, difpoints[i - 1], difpoints[i] - 1, padding, flag);
                        }
                        _GenInst(insts, terminates, difpoints[i - 1], right, padding, flag);
                    }
                    // 分割成子块来处理,所以不需要POP
                    return;
                }
                // CASE 2 : 找到新的公共前缀时,需要将前缀内的结果用辅助栈存起来
                // 根据分歧点将所有终点进行划分,然后分别处理
                // 并根据分歧点的数量来决定辅助栈的操作
                else
                {
                    //_GenInst(insts, terminates[left].Expr, old_padding, padding - 2, flag);

                    /*
                     * 如果更新的这一段前缀中包含其中一些终点的坐标
                     * 如果不考虑这些坐标,相应的终点就会被忽略
                     * 需要分以下步骤来解决这个问题
                     *      1. 将所有坐标升序排序
                     *      2. 将这段前缀按照坐标进行分段,然后每一段算出结果后执行终点指令
                     */
                    // 查询所有符合条件的tpoint
                    List <TPoint> tpoints = new List <TPoint>();
                    for (i = left; i <= right; i++)
                    {
                        foreach (TPoint tp in terminates[i].TPoints)
                        {
                            if (tp.ExprIndex >= old_padding && tp.ExprIndex <= padding - 2)
                            {
                                tpoints.Add(tp);
                            }
                        }
                    }
                    // 如果存在
                    if (tpoints.Count > 0)
                    {
                        // 所有找到的坐标进行排序
                        tpoints.Sort();
                        // 计算第一个坐标前面的值,并运行对应的第一个终点指令
                        _GenInst(insts, terminates[left].Expr, old_padding, tpoints[0].ExprIndex, flag);
                        tpoints[0].PLCUnit.GenInst(insts, flag);
                        tpoints[0].enable = false;
                        for (i = 1; i < tpoints.Count; i++)
                        {
                            // 依次计算坐标间隔的表达式的值,并执行终点指令
                            _GenInst(insts, terminates[left].Expr, tpoints[i - 1].ExprIndex + 3, tpoints[i].ExprIndex, flag | FLAG_CALAND);
                            tpoints[i].PLCUnit.GenInst(insts, flag);
                            tpoints[i].enable = false;
                        }
                        // 计算最后一个坐标后到找到的前缀之间的表达式的值
                        _GenInst(insts, terminates[left].Expr, tpoints[i - 1].ExprIndex + 3, padding - 2, flag | FLAG_CALAND);
                    }
                    // 如果没有
                    else
                    {
                        // 直接计算当前前缀的表达式
                        _GenInst(insts, terminates[left].Expr, old_padding, padding - 2, flag);
                    }
                    // 如果不存在分歧点,直接输出所有结果指令
                    if (difpoints.Count() == 0)
                    {
                        for (i = left; i <= right; i++)
                        {
                            _GenInst(insts, terminates, i, i, padding + 1, flag | FLAG_CALAND);
                        }
                    }
                    // 以下为涉及到辅助栈的PLC代码生成
                    else
                    {
                        // 将计算结果暂存进辅助栈
                        InstHelper.AddInst(insts, "MPS");
                        // 生成第一组终点的PLC指令程序
                        _GenInst(insts, terminates, left, difpoints[0] - 1, padding + 1, flag | FLAG_CALAND);
                        for (i = 1; i < difpoints.Count; i++)
                        {
                            // 中间结果从辅助栈中取出来,恢复现场
                            InstHelper.AddInst(insts, "MRD");
                            // 依次生成下一组
                            _GenInst(insts, terminates, difpoints[i - 1], difpoints[i] - 1, padding + 1, flag | FLAG_CALAND);
                        }
                        // 最后一次用到辅助栈,所以直接弹出
                        InstHelper.AddInst(insts, "MPP");
                        // 生成最后一组
                        _GenInst(insts, terminates, difpoints[i - 1], right, padding + 1, flag | FLAG_CALAND);
                    }
                }
            }

            /*
             * 前面没有与运算和或运算的链接,这时需要将栈顶弹出
             * 要注意一些特殊的标志指令,这些指令不涉及到栈的操作
             */
            if ((flag & FLAG_CALAND) == 0 && (flag & FLAG_CALOR) == 0 &&
                insts.Count() > 0 && !insts.Last().Type.Equals("LBL") && !insts.Last().Type.Equals("NEXT"))
            {
                InstHelper.AddInst(insts, "POP");
            }
        }