본문 바로가기
Program/PLC

[LS PLC / C#] XGB/XGT FEnet Socket 통신 (5) - BIT 통신

by 냠만 2024. 3. 14.

[비트단 데이터 참조 방식]

 

이전글에서 소개했듯 이미 DLL에 구현되어 있지만 막상 사용하면 저는 데이터가 이상하게 들어오거나 읽어지지 않았기 때문에 별도의 방법으로 비트정보를 파악할 수 있는 방법을 소개하겠습니다.

 


비트단위 자체의 데이터를 참조하여 읽어올 수 있는 수단은 모든 PLC 통신에서 없는 것으로 알고 있습니다.
(혹시나 있다면 제가 잘못 아는 것이니 댓글로 알려주세요)
대부분 워드단위나 바이트단위의 데이터를 읽어와서 파싱 해서 사용합니다.


LS PLC에서 비트영역을 읽어올 수 있는 영역대가 별도로 존재하는 것인지는 공부가 더필요할 것 같습니다.
그런데 협업을 하는 입장에서 어드레스 맵을 구현 하는데 제어의 의견이 아무래도 많이 반영되다 보니 제어 측이 관리하기 편하게 구성하는 경우가 많습니다.
결론적으로 영역대를 고를 수 있는 케이스는 통신을 뚫으면서 많지 않을 거란 이야기입니다.

[Bit Read Convert]

 

저는 워드데이터를 읽어와서 해당 값을 비트로 컨버팅 했습니다.
먼저 읽어온 데이터를 컨버팅 하는 방식입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public bool ReadChangeWordToBit(int iWord)
{
    int[] iBitValue = new int[16];
    ushort[] usTemp = new ushort[16];
    for (int i = 0; i < 16; i++)
    {
        usTemp[i] = 0;
    }
 
    #region Convert Linq 활용 방법
    stopwatch.Restart();
    string numString = Convert.ToString(iWord, 2);
    numString = new string(numString.Reverse().ToArray());
    for (int i = 0; i < numString.Length; i++)
    {
        if (numString.Substring(i, 1== "1") usTemp[i] = 1;
    }
    stopwatch.Stop();
    Console.WriteLine("Convert Linq 활용 방법 : " + stopwatch.ElapsedMilliseconds.ToString());
    #endregion
 
 
 
    #region 2의 n제곱을 사용한 계산
    stopwatch.Restart();
    //ushort[] usTemp = new ushort[16];
    int _iWord = iWord;
    for (int i = 0; i < 16; i++)
    {
        usTemp[i] = 0;
    }
 
    if (_iWord % 2 == 1)
    {
        _iWord -= 1;
        usTemp[0= 1;
    }
    for (int i = 15; i > -1; i--)
    {
        if (_iWord >= Int32.Parse(Math.Pow(2, i).ToString()))
        {
            _iWord -= Int32.Parse(Math.Pow(2, i).ToString());
            usTemp[i] = 1;
        }
    }
    stopwatch.Stop();
    Console.WriteLine("2의 n제곱을 사용한 계산 : " + stopwatch.ElapsedMilliseconds.ToString());
    #endregion
 
    #region Enumerable.Range를 활용한 비트 연산
    stopwatch.Restart();
    bool[] bTemp = new bool[16];
    bTemp = Enumerable.Range(016).Select(bitIndex => 1 << bitIndex).Select(bitMask => (iWord & bitMask) == bitMask).ToArray();
 
    for (int i = 0; i < 16; i++)
    {
        if (bTemp[i]) iBitValue[i] = 1;
        else iBitValue[i] = 0;
    }
    stopwatch.Stop();
    Console.WriteLine("Enumerable.Range를 활용한 비트 연산 : " + stopwatch.ElapsedMilliseconds.ToString());
    #endregion
 
    return true;
}
 
cs

총 3가지 방법이 존재합니다.

1. Linq 활용 방법

2. 2의 n제곱을 사용한 계산

3. Enumerable.Range를 활용한 비트 연산

테스트 결과 3가지 모두 동일한 기능을 수행하며, 연산 시간도 측정 불가능할 수준으로 빠릅니다. 이해하시기 편한 방법으로 진행하시면 될 것 같습니다.

 


[Bit Write Convert]

 

두 번째는 쓰는 데이터를 컨버팅 하는 방식입니다.

1
2
3
4
5
6
7
8
9
10
11
12
public string WriteChangeBitToWord(int iAddressCount)
{
    ushort SendValue = 0;
    for (int i = 0; i < iBitCount; i++)
    {
        if (oBitValue[iAddressCount, i] == 1)
            SendValue += (ushort)Math.Pow(2, i);
    }
    usChangeBitToWordValue = SendValue;
    return SendValue.ToString();
}
 
cs

 

소스에 AddressCount는 연속 쓰기 워드단에 따라 배열을 분할하기 위해서 

 



[끝마치며 생각해 볼 점]

 

잘 모르는 제어분들은 소켓통신을 뚫는데 상시로 체크해야 되는 비트와 제어에 필요한 I/O를 같은 워드단에 묶어놓으시는 경우가 있습니다. 제어입장에서는 묶어놓는 게 관리하기가 편합니다.
그러면 데이터를 참조하는 클라이언트 입장에서는 컨버팅을 한번 더 하거나 분기처리를 해야 되니 반드시 하트비트 같은 상시체크가 필요한 비트는 별도 워드로 빼달라고 하셔야 합니다. 어차피 읽어온 데이터를 파싱 하는 건 동일하니 배열을 나누기 귀찮다 하시는 분은 그냥 한 개 워드로 묶어달라 하셔도 됩니다. 

HeartBeat (HB)를 예로 들어드리겠습니다.

15Bit 14Bit 13Bit 12Bit 11Bit 10Bit 9Bit 8Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Bit
- - - - - - - - - - COM RD OUT IN2 IN1 HB

1 워드에 몰려있는 케이스는 읽어온 데이터를 파싱 해서 배열 하나로 묶으면 됩니다. (16Length 배열 1개 필요)

15Bit 14Bit 13Bit 12Bit 11Bit 10Bit 9Bit 8Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Bit
- - - - - - - - - - - - - - - HB
- - - - - - - - - - - COM RD OUT IN2 IN1

다른 워드로 분기되어 있는 케이스는 읽어온 데이터를 파싱해서 배열 두 개로 나눠야 합니다. (16 Length 배열 2개 필요)

 

결과적으로 파싱을 한번 하냐 두 번 하냐 차이인데, 어차피 여러 데이터를 한 번에 읽어오기 때문에 파싱 하는 시간은 거의 차이가 없다고 생각해도 됩니다. 코딩하는 사람의 성향에 따라 편하신 대로 사용하시면 될 것 같습니다.