본문 바로가기
PYTHON(파이썬)/파이썬 활용

MCPROTOCOL을 이용한 미쓰비시 PLC 통신

by eplus 2024. 11. 1.

미쓰비시 PLC 통신을 위해 Python 프로그램으로 제작되었으며, PLC에서 데이터를 읽어와 파일에 저장하고 로그인을 관리하는 역할을 수행합니다. 주요 pymcprotocol기능을 사용하여 PLC와 통신을 구현하고 있으며, 이 프로그램의 주요 기능은 다음과 같습니다.

주요 기능 문장:

  1. 출력 및 파일 생성 :
    • makedir(directory)함수는 외부에서 생성됩니다.
    • makefile(file, sHead)함수는 파일이 작성되는 경우 헤더 정보를 추가합니다.
  2. 서버 메시지 출력 :
    • displaymsg(msg, imode)함수는 로그인 메시지를 콘솔에 출력하고 파일을 기록할 수 있습니다. imode값이 1이면 파일을 기록하는 데 사용됩니다.
  3. PLC연결 :
    • PLCCon(sIP, iport)PLC와 연결을 시도합니다. 연결에 실패하면 업무 시간을 응답하고 재시도합니다. 연결이 성공하면 PLC 연결 OK메시지를 출력합니다.
  4. 파일 및 데이터 이동 :
    • 프로그램 실행 시점의 날짜 정보를 바탕으로, 데이터 저장과 파일을 생성합니다.
    • PLC 헤더 및 초기 값 plcadrm.txt파일에서 시작되는 데이터입니다. 이 파일은 데이터에 대한 정보를 포함하는 것으로 보입니다.
  5. PLC와의 메인 루프 :
    • PLC와 연결 후 루프로 데이터를 처리하고 메인 루프를 실행합니다. 이 루프에서는 다음과 같은 작업을 수행합니다:
      • PLC에서 D11490위치부터 33개의 명령 데이터를 읽었습니다.
      • 읽은 데이터를 파일로 기록하고 로그를 남깁니다.
      • 특정 데이터에 따라 조건을 추가로 처리하여 생성합니다.
      • 주소로 D11579주소 1를 써서 PLC에 신호를 보냅니다.

코드 흐름 분석:

  1. 생성 :
    • 출력 및 파일을 준비하고 파일인을 설정 plcadrm.txt하고 PLC 데이터의 헤더와 값을 설정합니다.
    • PLC에 연결을 시도하여 성공하면 메인 루프로 존재합니다.
  2. 메인 루프 :
    • 메인 루프에서는 PLC에서 데이터를 입력하고, 로그인 파일과 CSV 파일로 저장합니다.
    • 데이터를 기반으로 하는 D11579작업을 수행합니다.
    • 데이터 읽기에 실패할 경우 재연결을 시도하며 일정 시간이 파일과 작성을 생성합니다.

주요 변수 및 의미:

  • sIP, iport : PLC의 IP 주소와 포트 번호입니다. 이 정보를 바탕으로 PLC에 연결을 시도합니다.
  • w_values ​​: PLC에서 가져온 데이터의 목록입니다.
  • sHead, sValue : plcadrm.txt파일에서 태그온 헤더 정보와 초기 값입니다. 데이터 구조를 정의하는 역할을 합니다.
  • iErr : PLC 통신을 하면 카운트됩니다. 180번 이상 발생하면 재연결을 시도합니다.
  • pymc3e : pymcprotocol.Type3E클래스의 참여로, PLC 통신을 담당합니다.
##########################################################
#  미쓰비시 PLC I/F  
#    DATE: 2023.04.21  BY: EPLUS
##########################################################
import pymcprotocol
import sys
import time
import os
from datetime import datetime
import keyboard

def makedir(directory):
    try:
        if not os.path.exists(directory):
            os.makedirs(directory)
    except OSError:
        print("Error: Failed to create the directory.")

def makefile(file, sHead):
    try:
        if not os.path.exists(file):
            file = open(file, 'w')
            for r in sHead:
                file.write(r + ',')
            file.write('\n')
            file.close()
    except OSError:
        print("Error: Failed to create the file.")
       
def displaymsg(msg, imode):
    now = datetime.now()
    sDate = now.date()
    sMsg = str(now) + ' : ' + msg
    print(sMsg)
    if imode == 1:
        file = open("c:/tong/Plog/" + str(sDate) + ".txt","a")
        file.write(sMsg + '\n')
        file.close    

def PLCCon(sIP, iport):
    # PLC연결 후 접속 오류 시 종료에서 계속 확인 ------------    
    pymc3e = pymcprotocol.Type3E()
    pymc3e.setaccessopt(commtype="binary")
    isCon = True
    while isCon:
        try:
            pymc3e.connect(sIP, iport)
            sMsg = '## PLC 연결 OK!!! ## _IP: ' + sIP
            displaymsg(sMsg, 1)
            isCon = False
        except:
            sMsg = '## PLC 연결 오류!!! ## _IP: ' + sIP
            displaymsg(sMsg, 0)
            #프로그램종료 -> 계속 진행
            ##quit()
            time.sleep(5)
            isCon = True        
               
# c:\tong\년월 디렉토리 생성
sMsg = "## 년월 디렉토리 확인 및 생성  ##"
displaymsg(sMsg, 1)

now = datetime.now()
sDate = now.date()
sdate = now.strftime('%Y%m%d')
sYYmm = now.strftime('%Y%m')
makedir("c:/tong/" + sYYmm)    

# PLC Head 및 소숫점 Read ( 11, 12 : OK, NG 99:Spare, 88: 생성일시, 0~9 : 소숫점 )
file = open("c:/work/plcadrm.txt","r")
sTemp = file.readlines()
i = 0
for sline in sTemp:
    i += 1
    sMsg = "## plcadrm Read  ## : " + sline
    displaymsg(sMsg, 1)    
    sL= sline.replace('\n','')
    if i == 1:
        sHead = sL.split(';')
    elif i == 2:
        sValue = sL.split(';')
file.close()

# 일자별 파일 생성
makefile("c:/tong/" + sYYmm + '/' + str(sDate) + '.csv', sHead)  
###################################################
# plc 연결
###################################################
sIP = "192.168.40.165"
iport = 1025
sMsg = '## PLC 연결 ##_IP: ' + sIP + '_PORT: ' + str(iport)
displaymsg(sMsg, 1)

###################################################
# PLC연결 후 접속  
###################################################
pymc3e = pymcprotocol.Type3E()
pymc3e.setaccessopt(commtype="binary")
isCon = True
while isCon:
    try:
        pymc3e.connect(sIP, iport)
        sMsg = '## PLC 연결 OK!!! ## _IP: ' + sIP
        displaymsg(sMsg, 1)
        isCon = False
    except:
        sMsg = '## PLC 연결 오류!!! ## _IP: ' + sIP
        displaymsg(sMsg, 0)
        #프로그램종료 -> 계속 진행
        ##quit()
        time.sleep(5)
        isCon = True    

#pymc3e.connect("192.168.205.1", 5001)
###################################################
# Main Loop - Get Value From PLC - Start
###################################################
i = 0
iErr = 0
sJ = ""
while True:
    sMsg = str(i) + '_## PLC Read...##'
    displaymsg(sMsg, 9)
   
    try:
        ###################################################
        # PLC 값 읽기 1
        ###################################################
        w_values = pymc3e.batchread_wordunits(headdevice="D11490", readsize=33)
        iErr = 0
    except:
        w_values = []
        sMsg ='## MC Protocol Error ##_Err_Cnt: ' + str(iErr)
        displaymsg(sMsg, 1)
        #프로그램종료-> 3분후 재접속 시도 --------------------------------
        #quit()
        time.sleep(1)
        iErr = iErr + 1
        if iErr > 180:
            makedir("c:/tong/" + sYYmm)
            makefile("c:/tong/" + sYYmm + '/' + str(sDate) + '.csv', sHead)    
            ##
            pymc3e = pymcprotocol.Type3E()
            pymc3e.setaccessopt(commtype="binary")
            isCon = True
            while isCon:
                try:
                    pymc3e.connect(sIP, iport)
                    sMsg = '## PLC 연결 OK!!! ## _IP: ' + sIP
                    displaymsg(sMsg, 1)
                    isCon = False
                except:
                    sMsg = '## PLC 연결 오류!!! ## _IP: ' + sIP
                    displaymsg(sMsg, 0)
                    #프로그램종료 -> 계속 진행
                    ##quit()
                    time.sleep(5)
                    isCon = True  
            ##
            iErr = 0
       
    now = datetime.now()
    sDate = now.date()
    sdate = now.strftime('%Y%m%d')
    sYYmm = now.strftime('%Y%m')
    print(str(now), w_values)        
   
    if len(w_values) == 0:
        sMsg = '## 값이 없습니다 ##'
        displaymsg(sMsg, 1)
        # 값이 없으면 - 오류로 0 전달
        if iErr == 0:
            pymc3e.batchwrite_wordunits(headdevice="D11579", values=[0])
    else:
        file = open("c:/tong/Plog/" + str(sDate) + ".txt","a")
        for r in w_values:
            file.write('['+str(r)+']')
        w_values2 = pymc3e.batchread_wordunits(headdevice="D11575", readsize=5)    
        file.write(str(w_values2[0])+',')
        file.write(str(w_values2[3])+',')
        file.write('\n')
        file.close()
        ###################################################
        # PLC 값 저장
        ###################################################
        iCnt = 0
        if sJ != str(w_values[0]):
            file = open("c:/tong/" + sYYmm + '/' + str(sDate) + '.csv',"a")
            for r in w_values:
                iV = int(sValue[iCnt])
                if iV == 0 or iV == 99:
                    file.write(str(r)+',')
                elif iV == 11:
                    if str(r) == '0':
                        file.write(str(r)+'(OK),')
                    else:
                        file.write(str(r)+'(NG),')
                elif iV == 12:
                    if str(r) == '1':
                        file.write(str(r)+'(OK),')
                    elif str(r) == '2':
                        file.write(str(r)+'(NG),')
                    else:
                        file.write(str(r)+'( ),')
                elif iV >= 1 and iV <= 9:   # 소숫점 처리
                        file.write(str(float(r) / 10 **iV) + ',')
                else: # 88 생성일시
                    file.write(str(now))
                iCnt = iCnt + 1
            w_values2 = pymc3e.batchread_wordunits(headdevice="D11575", readsize=5)    
            r2 = str(w_values2[0])
            if str(r2) == '0':
                file.write(r2+'(OK),')
            else:
                file.write(r2+'(NG),')      
            r2 = str(w_values2[3])          
            file.write(r2+',')
            file.write(str(now))
            file.write('\n')
            file.close()
            #
            sJ = str(w_values[0])    
    # 2 초 단위로 1 전송
    if divmod(i, 2)[1] == 0 and iErr == 0:
        pymc3e.batchwrite_wordunits(headdevice="D11579", values=[1])
        sMsg = '## D11579에 1 전송 ##'
        displaymsg(sMsg, 1)      
    #            
    i = i + 1
    if i > 3000:
        i = 0
        makedir("c:/tong/" + sYYmm)
        makefile("c:/tong/" + sYYmm + '/' + str(sDate) + '.csv', sHead)    
    # 1초 대기                
    time.sleep(1)
###################################################
# Main Loop - Get Value From PLC - END
###################################################

이 프로그램은 주로 제조 환경에서 PLC 데이터를 로드하고 CSV 파일로 기록하고 장비의 상태를 모니터링하기 위해 사용됩니다.

현재 LG전자 협력사에 적용중인 프로그램으로 초기 버젼입니다. 참조하여 미쓰비시 PLC I/F에 충분히 활용 가능합니다. 

 

C#으로 변환한 소스입니다.

using System;
using System.IO;
using System.Net.Sockets;
using System.Threading;
using System.Collections.Generic;
using McProtocolLib; // Add MC Protocol library

class MitsubishiPLCInterface
{
    static void Main(string[] args)
    {
        string directory = $"c:/tong/{DateTime.Now.ToString("yyyyMM")}";
        MakeDirectory(directory);

        string sIP = "192.168.40.165";
        int iport = 1025;

        DisplayMessage("## PLC 연결 ##_IP: " + sIP + "_PORT: " + iport, true);

        McProtocolTcp mcProtocol = new McProtocolTcp(sIP, iport); // Instantiate MC Protocol client
        while (true)
        {
            if (ConnectToPLC(mcProtocol))
            {
                DisplayMessage("## PLC 연결 OK!!! ## _IP: " + sIP, true);
                break;
            }
            else
            {
                DisplayMessage("## PLC 연결 오류!!! ## _IP: " + sIP, false);
                Thread.Sleep(5000);
            }
        }

        int i = 0;
        while (true)
        {
            DisplayMessage($"{i}_## PLC Read...##", false);
            try
            {
                // PLC 값 읽기
                List<int> w_values = BatchReadWordUnits(mcProtocol, "D11490", 33);
                if (w_values.Count == 0)
                {
                    DisplayMessage("## 값이 없습니다 ##", true);
                    BatchWriteWordUnits(mcProtocol, "D11579", new List<int> { 0 });
                }
                else
                {
                    // 데이터를 파일에 기록
                    string logPath = $"c:/tong/Plog/{DateTime.Now.ToString("yyyy-MM-dd")}.txt";
                    using (StreamWriter file = new StreamWriter(logPath, true))
                    {
                        foreach (var value in w_values)
                        {
                            file.Write("[" + value + "]");
                        }
                        file.WriteLine();
                    }
                }

                if (i % 2 == 0)
                {
                    BatchWriteWordUnits(mcProtocol, "D11579", new List<int> { 1 });
                    DisplayMessage("## D11579에 1 전송 ##", true);
                }
            }
            catch (Exception ex)
            {
                DisplayMessage($"## MC Protocol Error ##: {ex.Message}", true);
            }

            i++;
            if (i > 3000)
            {
                i = 0;
                MakeDirectory(directory);
            }

            Thread.Sleep(1000);
        }
    }

    static void MakeDirectory(string directory)
    {
        try
        {
            if (!Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: Failed to create the directory - {ex.Message}");
        }
    }

    static void DisplayMessage(string msg, bool writeToLog)
    {
        string sMsg = DateTime.Now.ToString() + " : " + msg;
        Console.WriteLine(sMsg);
        if (writeToLog)
        {
            string logPath = $"c:/tong/Plog/{DateTime.Now.ToString("yyyy-MM-dd")}.txt";
            using (StreamWriter file = new StreamWriter(logPath, true))
            {
                file.WriteLine(sMsg);
            }
        }
    }

    static bool ConnectToPLC(McProtocolTcp mcProtocol)
    {
        try
        {
            mcProtocol.Open();
            return true;
        }
        catch (Exception)
        {
            return false;
        }
    }

    static List<int> BatchReadWordUnits(McProtocolTcp mcProtocol, string headDevice, int readSize)
    {
        try
        {
            int[] readValues = mcProtocol.GetDevice(blockType: "D", headDeviceNo: int.Parse(headDevice.Substring(1)), points: readSize);
            return new List<int>(readValues);
        }
        catch (Exception)
        {
            return new List<int>();
        }
    }

    static void BatchWriteWordUnits(McProtocolTcp mcProtocol, string headDevice, List<int> values)
    {
        try
        {
            mcProtocol.SetDevice(blockType: "D", headDeviceNo: int.Parse(headDevice.Substring(1)), values.ToArray());
        }
        catch (Exception ex)
        {
            DisplayMessage($"## PLC Write Error ##: {ex.Message}", true);
        }
    }
}

728x90
반응형