NGMsoftware

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

    학습


    C# C# .NET 매크로 프로그램 만들기. (시작 시간 액션 1부)

    페이지 정보

    본문

    안녕하세요. 엔지엠소프트웨어입니다. 이전 시간에 지연 액션을 만들어봤는데요. 시작 시간은 지연 액션과 유사하지만, 특정 시간에 매크로가 동작할 수 있도록 하는 기능입니다. 엔지엠 6 버전에서는 시작 시간에 스크립트를 추가해야만 하는 불편함이 있습니다. 만약, 시작 시간에 뭔가 동작을 시켜야 한다면 시작 시간 액션 아래에 스크립트 실행 액션을 배치하고, 이 액션의 속성으로 비동기로 할지 동기로 할지만 선택하면 됩니다. 이런 방식이 좀 더 간단하고, 사용하기 쉽습니다.

     

    이전 버전의 액션들이 다소 중복되는 속성들과 많은 내용을 담고 있다보니 복잡도가 올라가고 사용 방법을 이해하는데 어려움이 많았습니다. 최신 버전을 다시 만들면서 이런 부분들을 모두 개선하고, 가능하면 편의 기능을 제공하기 보다는 로직을 쌓아가면서 처리할 수 있도록 하는게 좋은 방향이라고 생각합니다. 이전 액션인 지연에서도 설명했듯이 여기에도 있고, 저기에도 같은 내용이 있더라도 프로세스 관점에서는 타이밍이라는것 때문에 다르게 동작할 수 있습니다. 이런 부분들은 매크로 스크립트를 제작할 때 혼란을 불러오고, 스크립트를 수정했을 때 되던게 안될수도 있습니다.

     

    문제를 해결하는 가장 쉬운 방법은 스크립트를 간단하게 그리고, 직관적으로 이해할 수 있게 만드는겁니다. 실행 전 지연이나 실행 후 지연을 사용하기 보다는 액션 스택이 바로 보이도록 지연 액션을 사용하는게 좋다는 의미입니다.

     

    날짜와 시간 관련 액션들을 모아둘 폴더를 하나 만들고, StartDateTimeModel을 추가했습니다.

    sUAXDAE.jpeg

     

     

    시작 시간 액션은 아래와 같은 속성을 가지고 있습니다.

    [LocalizedCategory("Action")]
    [LocalizedDisplayName("DateTimeFormat")]
    [LocalizedDescription("DateTimeFormat")]
    [Browsable(true)]
    [DefaultValue("yyyy-MM-dd HH:mm:ss.fff")]
    public string DateTimeFormat { get; set; } = "yyyy-MM-dd HH:mm:ss.fff";
    
    [LocalizedCategory("Action")]
    [LocalizedDisplayName("StartDateTime")]
    [LocalizedDescription("StartDateTime")]
    [Browsable(true)]
    [DefaultValue(null)]
    [EditorAttribute(typeof(MultilineStringEditor), typeof(UITypeEditor))]
    public string? StartDateTime { get; set; }
    
    [LocalizedCategory("Action")]
    [LocalizedDisplayName("UseCron")]
    [LocalizedDescription("UseCron")]
    [Browsable(true)]
    [DefaultValue(false)]
    public bool UseCron { get; set; }
    
    [LocalizedCategory("Cron")]
    [LocalizedDisplayName("CronJob")]
    [LocalizedDescription("CronJob")]
    [Browsable(true)]
    [DefaultValue("* * * * * *")]
    public string CronJob { get; set; } = "* * * * * *";
    
    [LocalizedCategory("Cron")]
    [LocalizedDisplayName("TimeZone")]
    [LocalizedDescription("TimeZone")]
    [Browsable(true)]
    [DefaultValue(typeof(Ai.Definition.TimeZone), "Local")]
    public Ai.Definition.TimeZone TimeZone { get; set; } = Definition.TimeZone.Local;

     

    속성이 많아 보일수도 있지만, 카테고리로 구분되어 있어서 사용하는데 어렵지는 않을겁니다.

    n7OZbtZ.jpeg

     

     

    이 액션은 활용성을 극대화 하기 위해 쉬운 버전과 복잡한 버전 2가지로 구성되어 있습니다. 개발자들은 크론잡을 주로 사용하기 때문에 쉽게 이해할 수 있지만, 일반인들은 크론을 사용하기가 어려울 수 있습니다. 따라서, 아래와 같이 간단하게 날짜와 시간을 입력해서 처리가 가능합니다.

    • 날짜 시간 형식: yyyy-MM-dd HH:mm:ss.fff
    • 시작 시간: 2024-04-20 17:30:00.000

     

    위와 같이 설정하면 해당 시간에 한번 매크로가 실행됩니다. 매일 같은 시간에 실행되도록 하려면 아래와 같이 설정하면 됩니다.

    • 날짜 시간 형식: HH:mm:ss
    • 시작 시간: 17:30:00

     

    참고로, 마지막 Millisecond는 0.001까지 설정하면 동작하지 않을수도 있습니다. 최소 단위는 0.01까지입니다. 시간을 비교할 때 윈도우에 행이 걸리지 않도록 내부적으로 지연이 걸려 있습니다. 이 지연은 최소 단위가 0.001이기 때문에 여기까지 체크하려면 다 작은 단위의 Timeset이 필요한데요. 윈도우에서 제공하는 Timeset은 최대 1/1000초입니다. 따라서, 0.001초 간격으로 비교할 때 지연에서 0.001초를 소모해서 이미 타임이 넘어갔을 수 있습니다. 이런 경우에는 비교가 실패하게 됩니다.

     

    가장 안정적으로 시작 시간을 사용하려면 아래와 같이 초까지만 설정하는게 좋습니다.

    • 날짜 시간 형식: HH:mm:ss
    • 시작 시간: 17:30:00

     

    실행 로직은 간단합니다. 일단 크론 사용 여부에 따라서 로직이 분리되어 있고, 위에서 주의사항을 말했듯이 0.001초 체크에 대한 문제점을 코드로 확인할 수 있습니다.

    public override string? Execute(IPlayer player)
    {
        var id = base.Execute(player);
    
        if (UseCron)
        {
            if (!Ai.Common.Helper.NullCheckAndWriteLine(player, nameof(CronJob), CronJob))
                return id;
    
            TimeZoneInfo timeZoneInfo = TimeZoneInfo.Local;
    
            switch (TimeZone)
            {
                case Definition.TimeZone.Utc:
                    timeZoneInfo = TimeZoneInfo.Utc; 
                    break;
            }
    
            var dt = new Ai.Api.ApiHelper().GetCronJob(CronJob, timeZoneInfo);
    
            while (System.DateTime.Now.ToString("yyyyMMddHHmmss") != dt?.ToString("yyyyMMddHHmmss"))
            {
                if (player.CT.IsCancellationRequested)
                    break;
    
                Task task = Task.Delay(1, player.CT);
    
                try
                {
                    task.Wait();
                }
                catch
                {
                    break;
                }
            }
        }
        else
        {
            if (!Ai.Common.Helper.NullCheckAndWriteLine(player, nameof(DateTimeFormat), DateTimeFormat))
                return id;
    
            if (!Ai.Common.Helper.NullCheckAndWriteLine(player, nameof(StartDateTime), StartDateTime))
                return id;
    
            while (System.DateTime.Now.ToString(DateTimeFormat) != StartDateTime)
            {
                if (player.State != Definition.PlayerState.Play)
                    break;
    
                Task task = Task.Delay(1);
    
                try
                {
                    task.Wait();
                }
                catch
                {
                    break;
                }
            }
        }
    
        return id;
    }

     

    아래와 같이 스크립트를 구성하고, 매크로를 실행하면 오후 5시 30분에 클릭이 실행됩니다.

    CKOfpqw.jpeg

     

     

    이번에는 크론 사용을 True로 변경하고, 크론잡을 변경해가면서 테스트 해볼 수 있습니다. 크론잡을 등록하는 방법은 아래와 같습니다.

    ┌───────────── second (optional)       0-59              * , - /                      
    │ ┌───────────── minute                0-59              * , - /                      
    │ │ ┌───────────── hour                0-23              * , - /                      
    │ │ │ ┌───────────── day of month      1-31              * , - / L W ?                
    │ │ │ │ ┌───────────── month           1-12 or JAN-DEC   * , - /                      
    │ │ │ │ │ ┌───────────── day of week   0-6  or SUN-SAT   * , - / # L ?                Both 0 and 7 means SUN
    │ │ │ │ │ │
    * * * * * *

     

    모든 필드에서 숫자를 사용하여 필드를 임의의 값 * 으로 표시하고 값 범위를 지정할 수 있습니다. 22-1(와 동일 22,23,0,1,2)과 같은 역방향 범위 도 지원됩니다. /를 *, 숫자 및 범위와 결합하여 단계를 정의할 수 있습니다. 예를 들어 분 필드의 */5는 5분마다, 일 필드의 1-15/3은 1일부터 15일까지 3일마다를 나타냅니다. */24는 0,24,48과 동일하며 분 필드의 */24는 문자 그대로 24분마다를 의미하는 것이 아니라 0,24,48분마다를 의미합니다.

     

    값과 범위를 ,로 연결합니다. 쉼표는 OR 연산자처럼 작동합니다. 따라서 3,5-11/3,12는 3,5,8,11,12와 같습니다. 월 및 요일 필드에서는 숫자 값 대신 처음 세 글자(Jan-Dec 또는 Mon-Sun)로 축약된 월 또는 요일 이름을 사용할 수 있습니다. JANUARY 또는 MONDAY와 같은 전체 이름은 지원되지 않습니다. 요일 필드의 경우 0과 7은 모두 일요일에, 1은 월요일에 유지됩니다.

    Expression Description
    * * * * * Every minute
    0 0 1 * * At midnight, on day 1 of every month
    */5 * * * * Every 5 minutes
    30,45-15/2 1 * * * Every 2 minute from 1:00 AM to 01:15 AM and from 1:45 AM to 1:59 AM and at 1:30 AM
    0 0 * * MON-FRI At 00:00, Monday through Friday

     

    대부분의 표현식은 기본 문자를 사용하여 설명할 수 있습니다. 달의 마지막 날이나 두 번째 토요일과 같이 더 복잡한 경우를 처리하려면 특수 문자를 사용하세요. L은 "마지막"을 의미합니다. 요일 필드에 사용되면 마지막 금요일(5Lor FRIL)과 같은 구성을 지정할 수 있습니다. day-of-month 필드에는 해당 월의 마지막 날을 지정합니다.

     

    일자 필드의 W는 가장 가까운 평일입니다. 단일 값(범위, 단계 또는 * 아님)과 함께 W를 사용하여 지정된 날짜에 가장 가까운 평일을 정의합니다. 이 경우 발생 여부를 결정하는 두 가지 기본 규칙이 있습니다. 가장 가까운 평일로 이동해야 하며 다른 달로 이동할 수는 없습니다. 따라서 주어진 요일이 토요일이면 금요일로 이동하고, 일요일이면 월요일로 이동합니다. 그러나 주어진 날짜가 월의 1일(예: 0 0 1W * *)이고 토요일이면 세 번째 월요일로 이동하고, 주어진 날짜가 월의 마지막 날(0 0 31W 0 0)이고 일요일이면 이동합니다. 그 금요일까지. L(선택적으로 오프셋 포함) 및 W 문자를 혼합하여 LW 달의 마지막 주일 또는 L-5W와 같이 더 복잡한 날짜를 지정합니다.

     

    요일 필드의 #을 사용하면 두 번째 토요일(6#2 또는 SAT#2)과 같은 구성을 지정할 수 있습니다. ? *의 동의어입니다. 지원되지만 필수는 아니므로 0 0 5 * ? 0 0 5 * *와 같습니다.

    Expression Description
    0 0 L * * At 00:00 AM on the last day of the month
    0 0 L-1 * * At 00:00 AM the day before the last day of the month
    0 0 3W * * At 00:00 AM, on the 3rd weekday of every month
    0 0 LW * * At 00:00 AM, on the last weekday of the month
    0 0 * * 2L At 00:00 AM on the last tuesday of the month
    0 0 * * 6#3 At 00:00 AM on the third Saturday of the month
    0 0 ? 1 MON#1 At 00:00 AM on the first Monday of the January

     

    날짜와 요일을 모두 설정할 수 있으며, 이를 통해 13일의 금요일과 같은 구문을 지정할 수 있습니다. 따라서 0 0 13 * 5는 13일 금요일 00:00을 의미합니다. Unix crontab 및 Quartz cron 구현과 다릅니다. Crontab은 이를 OR 연산자처럼 처리합니다. 특정 날짜 또는 특정 요일에 발생이 발생할 수 있습니다. 따라서 0 0 13 * 5는 매주 금요일 또는 매월 13일 오전 00:00를 의미합니다. Quartz는 날짜와 요일을 모두 지정할 수 없습니다.


    매크로는 @로 시작하는 문자열이며 매일 또는 매분과 같은 간단한 경우에 대한 단축키를 나타냅니다.

    Macro Equivalent Comment
    @every_second * * * * * * Run once a second
    @every_minute * * * * * Run once a minute at the beginning of the minute
    @hourly 0 * * * * Run once an hour at the beginning of the hour
    @daily 0 0 * * * Run once a day at midnight
    @midnight 0 0 * * * Run once a day at midnight
    @weekly 0 0 * * 0 Run once a week at midnight on Sunday morning
    @monthly 0 0 1 * * Run once a month at midnight of the first day of the month
    @yearly 0 0 1 1 * Run once a year at midnight of 1 January
    @annually 0 0 1 1 * Run once a year at midnight of 1 January

     

    개발자에게 후원하기

    MGtdv7r.png

     

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

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

    감사합니다~

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

    댓글목록

    등록된 댓글이 없습니다.