NGMsoftware

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

    학습


    C# C# .NET 매크로 프로그램 만들기. (매크로 개발 속도를 올려주는 편의 도구 디버그 기능 1부)

    페이지 정보

    본문

    안녕하세요. 엔지엠소프트웨어입니다. 프로그램 또는 소프트웨어 개발의 꽃이라고 불리우는 디버그 기능을 개발해볼텐데요. 프로그래머 또는 개발자가 아니더라도 엔지엠 매크로를 이용해서 업무 자동화를 만들 때 디버깅 기능이 필요합니다. 개발자분들은 디버깅이 뭔지 알겠지만, 비개발자 분들은 용어는 들어봤어도 정확하게 어떤건지는 잘 모를 수 있어요.

     

    엔지엠 매크로의 디버그는 엄밀히 말하는 트레이스(Trace)에 가깝습니다. 전문 소프트웨어 개발툴에 비하면 많이 부족하지만, 하나씩 기능을 추가하면서 개선해 나가면 꽤 많은 도움이 되는 기능이 될거 같아요. 우선, 오늘 1부에서는 실제로 사용되는 변수의 값을 어떻게 트레이스 하는지 알아보고 다음에는 변수의 값을 변경하는 방법에 대해서 알아볼께요. 이와 관련된 내용이 길어질듯해서 1부, 2부, 3부로 나눠서 작성해야 할듯 합니다. 어쩌면, 더 많아질수도 있을거 같아요.

     

    가장 먼저 해야 할 일은 사용자가 컨트롤할 수 있는 윈도우 컨트롤이 필요하겠죠? 기능 확장을 위해서 상단에 빈 공간을 남겨두었습니다.

    63ALyRh.jpeg

     

     

    지금 가장 큰 고민은 매크로 실행을 트레이스할 수 있는 출력창 정보와 변수 정보를 플레이어에서도 제공해야 하나입니다. 아무래도, 업무 자동화 매크로를 제작해서 판매하시는 제작자분들도 있다보니 너무 많은 정보를 노출하면 안되기 때문입니다. 스크립트와 이미지를 암호화하면 이런 로그성 정보도 노출하지 않기를 원하니까요. 그리고, 이전 버전에서 문제가 되었던 배포인데요. 아무래도 이전 문제를 개선해야 하기 때문에 스크립트와 이미지들을 한번에 묶어서 처리할 수 있는 플레이어도 필요할거 같아요.

     

    이제 만든 디버그 창을 메뉴에 추가해야 하는데요. 메뉴에 추가하는 방법은 이전 글을 참고하시면 됩니다. 일단, 독(Dock) 컨트롤이기 때문에 아래와 같이 Show / Hide도 처리해줄께요. 아래와 같이 메뉴 처리 라이브러리에서 속성을 추가 해줍니다.

    public bool ShowDebug
    {
        get
        {
            if (_client is IEditor)
            {
                return ((KryptonRibbonGroupButton)
                    ((KryptonRibbonGroupLines)
                    ((IEditor)_client).Ribbon.RibbonTabs[4].Groups[1].Items[0])
                    .Items[7]).Checked;
            }
            else
            {
                return false;
            }
        }
    }

     

    이제부터 좀 복잡한 내용인데요. 변수를 처리하는 프로세스는 매크로 엔진의 플레이어가 처리하고 있습니다. 그런데, 플레이어는 매크로가 실행되는 시점에 동적으로 생성되고, 매크로가 완료되면 자동으로 삭제됩니다. 따라서, 클라이언트에서 스크립트를 만들었다고 해서 플레이어의 정보를 가져올 수 없습니다. 따라서, 플레이어가 메모리에 로드되는 시점에 항상 존재하는 클라이언트의 컨트롤에 접근할 수 있어야 합니다.

     

    아래와 같이 클라이언트가 실행될 때 디버그 독 컨트롤을 생성해줍니다.

    new Model.Client.DockModel()
    {
        Name = typeof(Ai.Common.Client.Dock.Debug).Name,
        Text = ResxCaption.GetString(Ai.DockName.Debug) ?? string.Empty,
        TextTitle = ResxCaption.GetString(Ai.DockName.Debug) ?? string.Empty,
        Control = new Ai.Common.Client.Dock.Debug(this),
        Visible = true,
        Description = ResxMessage.GetString(Ai.DockName.DebugDescription)
    }

     

    디버그 독을 플레이어가 가져올 수 있도록 클라이언트에 속성을 만들어줍니다.

    [ReadOnly(true)]
    public Ai.Interface.IDebug Debug
    {
        get
        {
            return (Ai.Interface.IDebug)_dockHelper.GetDockControl<Ai.Common.Client.Dock.Debug>(nameof(Ai.Common.Client.Dock.Debug));
        }
    }

     

    플레이어가 로딩되면 아래와 같이 사용 여부를 확인하고, 디버그 독을 가져옵니다.

    if (Manager.Client.Debug.IsDebugEnabled)
    {
        var vars = actions.Where(w => w is Model.Action.Variables.AddVariableModel).Cast<Ai.Interface.IModel>().ToList();
        Manager.Client.Debug.Run(this, vars);
    }

     

    디버그 독을 가져와서 Run 메소드를 호출하면 비로소 변수를 트레이스 할 수 있게됩니다.

    public void Run(Ai.Interface.IPlayer player, List<Ai.Interface.IModel> models)
    {
        _rows.Clear();
        this.dgvVariables.Rows.Clear();
    
        var vars = models.Cast<Ai.Model.Action.Variables.AddVariableModel>();
    
        foreach (var v in vars)
        {
            int rowIndex = this.dgvVariables.Rows.Add(v.ID, v.IsGlobal, v.IsParent, v.IsConstant, v.DefaultValue, null, null);
            _rows.Add(this.dgvVariables.Rows[rowIndex]);
        }
    
        _player = (Ai.Engine.Player)player;
        _player.VariableChanged += P_VariableChanged;
        _player.StatusChanged += P_StatusChanged;
    }

     

    디버그 트레이스가 시작되면 플레이어의 이벤트를 할당하고, 추적하도록 해야 합니다. 플레이어에서 제공하는 이벤트는 아래와 같이 4가지입니다.

    public event EventHandler<Ai.Events.ActionArgs>? BeforeCurrentAction;
    public event EventHandler<Ai.Events.ActionArgs>? AfterCurrentAction;
    public event EventHandler<Ai.Events.PlayerStatusArgs>? StatusChanged;
    public event EventHandler<Ai.Events.VariableChangeArgs>? VariableChanged;

     

    플레이어의 상태 변화를 이벤트로 구독하고, 플레이어가 실행중일 때만 변수를 트레이스합니다. 그리고, 플레이어가 종료되면 아래와 같이 할당된 이벤트도 모두 제거해야 합니다. 만약, 할당된 이벤트를 해제하지 않으면 이벤트가 중복 적용되어서 하나의 이벤트가 여러번 실행될 수 있습니다. 이벤트는 누적되기 때문에 추가하고, 사용하지 않는다면 반드시 제거해줘야 합니다.

    private void P_StatusChanged(object? sender, Events.PlayerStatusArgs e)
    {
        if (e.Status == Definition.PlayerState.Stop)
        {
            _player.VariableChanged -= P_VariableChanged;
            _player.StatusChanged -= P_StatusChanged;
        }
    }

     

    변수의 값이 변화해서 이벤트가 발생하면 아래와 같이 데이타를 받을 수 있습니다.

    private void P_VariableChanged(object? sender, Events.VariableChangeArgs e)
    {
        var row = _rows.Where(w => w.Cells[0].Value.ToString() == e.VariableName).FirstOrDefault();
    
        if (row != null)
        {
            row.Cells["NewValue"].Value = e.NewValue;
            row.Cells["OldValue"].Value = e.OldValue;
        }
    }

     

    이벤트 데이타는 VariableChangeArgs 클래스에 담겨서 넘어오는데요. 이 클래스는 변수 모델과 유사합니다. 동일한 데이타를 사용하기 때문입니다.

    namespace Ai.Events
    {
        public class VariableChangeArgs : EventArgs
        {
            public string? VariableName { get; set; }
            public object? OldValue { get; set; }
            public object? NewValue { get; set; }
            public bool IsGlobal { get; set; }
            public bool IsParent { get; set; }
        }
    }

     

    이제 테스트용 스크립트를 만들어 볼께요. 변수는 총 3개를 추가했는데요. 여기서 하나만 사용할거예요. a 변수의 초기 값을 0으로 설정합니다.

    9UmNuGn.jpeg

     

     

    그룹과 액션이동으로 무한 반복 루틴을 만들었습니다. 그리고, 너무 빠르게 동작하면 확인하기 어려우니 1초 지연을 줬어요.

    THg1KJH.jpeg

     

     

    사칙연산 액션으로 변수 값을 1씩 증가 시켜줬습니다.

    4cVT1fM.jpeg

     

     

    증감한 결과 값을 변수 a에 업데이트 해줍니다.

    xMxGo3J.jpeg

     

     

    매크로를 실행하고, 테스트 결과를 보겠습니다.

     

     

    개발자에게 후원하기

    MGtdv7r.png

     

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

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

    감사합니다~

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

    댓글목록

    등록된 댓글이 없습니다.