NGMsoftware

NGMsoftware
로그인 회원가입
  • 매뉴얼
  • 학습
  • 매뉴얼

    학습


    C# C# .NET 매크로 프로그램 만들기. (날짜 시간 체크 액션 만들기)

    페이지 정보

    본문

    안녕하세요. 엔지엠소프트웨어입니다. 오늘은 날짜 시간 체크 액션을 만들어 볼건데요. 매크로 프로그램을 사용하면서 가장 많이 하는 질문중에 하나가 특정 시간에 매크로가 동작할 수 있는지인데요. 쉽게 말해서 타이머 또는 예약과 같은 것이라고 생각하시면 됩니다. 업무가 정해진 시간에 시작된다면, 특정 시간에 매크로가 실행되어야 합니다. 엔지엠 6에서는 스크립트 예약이 별도의 스레드로 스크립트를 불러오다보니 생각보다 많은 리소스를 사용하게 되고, 많은 스레드를 생성할 때 문제가 발생하는 경우들이 많았습니다. 다음에 알아볼 예약 작업의 경우 이 부분을 상당부분 개선하도록 할 예정입니다.

     

    시간을 처리하는 가장 쉬운 방법을 아직도 고민중이고, 이런저런 테스트를 거치면서 디자인을 만들어가고 있는데요. 어떤식으로 만들지가 결정되면 디자인하고, 테스트 후 다시 글을 남길 수 있을듯 합니다. 아무튼, 오늘은 좌항과 우항의 시간을 비교하는 조건만 알아보도록 하겠습니다.

     

    DateTimecheckModel은 날짜와 시간을 비교할 수 있는 액션입니다. 물론, 날짜 또는 시간만 비교할수도 있습니다. 기존의 엔지엠 6 매크로 방식은 너무 복잡해서 사용하기가 어렵고, 난해한 부분들이 있었습니다. 이번에는 이런 부분들을 과감하게 걷어내고, 쉽게 이해하고 사용할 수 있도록 구성했습니다.

    public class DateTimeCheckModel : CheckModel

     

    체크 액션들은 모두 BaseModel을 상속 받아야 하고, 아래와 같이 좌항과 우항의 값을 담당하는 속성을 사용할 수 있어야 합니다.

    [LocalizedCategory("Action")]
    [LocalizedDisplayName("LeftValue")]
    [LocalizedDescription("LeftValue")]
    [Browsable(true)]
    [DefaultValue(false)]
    [EditorAttribute(typeof(MultilineStringEditor), typeof(UITypeEditor))]
    public string? LeftValue { get; set; }
    
    [LocalizedCategory("Action")]
    [LocalizedDisplayName("RightValue")]
    [LocalizedDescription("RightValue")]
    [Browsable(true)]
    [DefaultValue(false)]
    [EditorAttribute(typeof(MultilineStringEditor), typeof(UITypeEditor))]
    public string? RightValue { get; set; }

     

    체크의 종류는 많습니다. 문자가 될수도 있고, 숫자 또는 날짜가 될수도 있습니다. 이외에도 다양한 것들을 비교하고 같은지 체크할 수 있는데요. 이런것들을 처리하기 위해서는 문자 타입이 가장 적절하다는 판단입니다. 무엇보다 중요한건 플레이스홀더 기능을 사용하기 위함입니다. 대신에 몇가지 포기해야 하는것들도 있는데요. 이 부분은 어쩔 수 없는 부분들입니다. 얻는게 있으면 포기해야 하는것들도 있으니까요.

     

    날짜 시간 체크 액션에서 가장 중요한 속성은 날짜 시간 형식입니다.

    [Category("Action")]
    [LocalizedDisplayName("DateTimeFormat")]
    [LocalizedDescription("DateTimeFormat")]
    [Browsable(true)]
    [DefaultValue("yyyyMMddHHmmssfff")]
    public string DateTimeFormat { get; set; } = "yyyyMMddHHmmssfff";

     

    날짜 시간 형식에 따라서 비교할 값들이 모두 형식에 맞게 변환되기 때문입니다. 예를 들어서 아래와 같이 좌항과 우항이 있다고 생각 해보세요.

    • 좌항: 2024.4.12 12:00:00
    • 우항: 2024.5.5 12:00:00
    • 형식: HHmmss

     

    위의 좌항과 우항은 날짜만 다르고, 시간은 같습니다. 하지만, 형식이 HHmmss와 같이 시분초로 되어 있기 때문에 자동으로 년월일은 사라지고, 시분초만 가지고 서로 비교합니다. 그래서, 결과는 True가 됩니다. 매크로를 실행하고 테스트 스크립트를 만들어서 실행 해보면 동일한 결과를 확인할 수 있습니다.

    BSe2zjf.jpeg

     

     

    날짜 시간 형식을 아래와 같이 변경하면, 날짜까지도 비교하게 됩니다. 이러면 결과는 False가 됩니다.

    • 형식: yyyyMMddHHmmss

    5bqgi3D.jpeg

     

     

    날짜 시간 비교 옵션을 GreaterThan으로 선택하고, 값을 변경해보세요. 좌항(왼쪽 값)이 크므로 True가 됩니다. 이렇게 시간만 비교할 수 있습니다.

    J2aUVJb.jpeg

     

     

    빈 값 현재 시간 사용을 True로 변경하면 좌항과 우항에 값이 없을 때 현재 날짜와 시간으로 자동 설정됩니다. 따라서 아래와 같이 비교할 수 있습니다.

    5Vo5bgJ.jpeg

     

     

    여기서 중요한 점은 날짜 시간 형식은 컴퓨터가 구분할 수 있게 형식을 작성하는게 중요합니다. 

    • 2024.12.12 15:05.01
    • 2024-09-17 09:05.01

     

    아래와 같이 단순히 숫자만 나열하는 경우 날짜 형식의 어느 위치인지 컴퓨터는 알 수 없습니다. 월일인지 시분인지 분초인지 확인이 불가능하여 연산이 안됩니다.

    • 1212
    • 0819

     

    시분을 비교하면 형식은 HH:mm이 되고, 분초는 mm.ss가 됩니다. 형식도 비교하려는 날짜 또는 시간에 맞게 설정해야 정상적으로 사용할 수 있습니다. 이제 마지막으로 비교 연산자의 로직을 살펴볼까요? 내용이 다소 복잡하고 비효율적이긴 하지만, 사용자의 편의를 위해 어느정도 코딩이 필요합니다.

    public override string? Execute(Ai.Interface.IPlayer player)
    {
        string? id = base.Execute(player);
    
        string leftValue = Ai.Common.Helper.GetMatches(player, this.GetType().GetProperty(nameof(LeftValue)), LeftValue);
        string rightValue = Ai.Common.Helper.GetMatches(player, this.GetType().GetProperty(nameof(LeftValue)), RightValue);
    
        if (UseNullToCurrentTime)
        {
            if (string.IsNullOrEmpty(leftValue))
                leftValue = System.DateTime.Now.ToString(DateTimeFormat);
    
            if (string.IsNullOrEmpty(rightValue))
                rightValue = System.DateTime.Now.ToString(DateTimeFormat);
        }
        else
        {
            if (!Ai.Common.Helper.NullCheckAndWriteLine(player, GetType().GetProperty(nameof(LeftValue)).Name, leftValue))
                return id;
    
    
            if (!Ai.Common.Helper.NullCheckAndWriteLine(player, GetType().GetProperty(nameof(RightValue)).Name, rightValue))
                return id;
        }
    
        try
        {
            leftValue = System.DateTime.Parse(leftValue).ToString(DateTimeFormat);
            rightValue = System.DateTime.Parse(rightValue).ToString(DateTimeFormat);
    
            switch (CheckOption)
            {
                case Definition.NumberCheckOption.EqualsGreaterThan:
                    ConditionResult = System.DateTime.ParseExact(leftValue, DateTimeFormat, null) >= 
                                      System.DateTime.ParseExact(rightValue, DateTimeFormat, null);
                    break;
                case Definition.NumberCheckOption.GreaterThan:
                    ConditionResult = System.DateTime.ParseExact(leftValue, DateTimeFormat, null) > 
                                      System.DateTime.ParseExact(rightValue, DateTimeFormat, null);
                    break;
                case Definition.NumberCheckOption.EqualsLessThan:
                    ConditionResult = System.DateTime.ParseExact(leftValue, DateTimeFormat, null) <= 
                                      System.DateTime.ParseExact(rightValue, DateTimeFormat, null);
                    break;
                case Definition.NumberCheckOption.LessThan:
                    ConditionResult = System.DateTime.ParseExact(leftValue, DateTimeFormat, null) < 
                                      System.DateTime.ParseExact(rightValue, DateTimeFormat, null);
                    break;
                case NumberCheckOption.Equals:
                    ConditionResult = System.DateTime.ParseExact(leftValue, DateTimeFormat, null) == 
                                      System.DateTime.ParseExact(rightValue, DateTimeFormat, null);
                    break;
            }
    
            if (ConditionResult)
            {
                id = this.TrueID;
    
                if (Actions != null)
                    Actions[0].IsSkip = false;
    
                if (Actions != null)
                    Actions[1].IsSkip = true;
            }
            else
            {
                id = this.FalseID;
    
                if (Actions != null)
                    Actions[0].IsSkip = true;
    
                if (Actions != null)
                    Actions[1].IsSkip = false;
            }
        }
        catch (Exception ex)
        {
            player.Manager.Output.ErrorWriteLine(ex.Message);
        }
    
        return id;
    }

     

    코드만 길뿐 내용은 단순합니다. 문자열을 DateTime 형식으로 변경한 후 포멧에 맞게 변환해줍니다. 변환된 값으로 좌항과 우항을 셋팅하고, 이 값으로 같거나 큰지 또는 작은지 비교하는 로직입니다. 사용자로부터 데이터를 받을 때 DateTime 형식으로 받았다면 이런 캐스팅 부분들이 전부 필요가 없는데요. 문자열로 처리할 수 있는 다양한 편의 기능 때문에 약간의 오버헤드는 포기해야 합니다.

     

    다음에는 예약 작업을 좀 더 쉽게할 수 있는 액션을 만들어보겠습니다. 이 기능을 만들면, 이 후에 만들 동시 실행 액션들도 어느정도 디자인이 나올듯 합니다. 기본적인 뼈대는 비슷하게 가려가려고 하거든요. 기존 엔지엠 6처럼 스크립트 파일을 불러와서 별도의 스레드에 할당하는게 아닌 액션들의 집합을 이용하는 방향으로 가려고 합니다. 일단, 아직은 고민을 좀 더 해봐야겠습니다.

     

    개발자에게 후원하기

    MGtdv7r.png

     

    추천, 구독, 홍보 꼭~ 부탁드립니다.

    여러분의 후원이 빠른 귀농을 가능하게 해줍니다~ 답답한 도시를 벗어나 귀농하고 싶은 개발자~

    감사합니다~

    • 네이버 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 카카오스토리 공유하기
    추천0 비추천0

    댓글목록

    등록된 댓글이 없습니다.