퇴근5분전

 

오늘 PLC 모니터링 기능을 추가하면서 웹 테스트를 하는데... 

이상한 현상이???

 

.razor 안에서 ... 

@code{
	MonitoringInfo info = null;
}


@foreach( var brd in bridges ) {
    info = Service.GetMonitoringInfo( brd );
	
    <div ... >
    	<button @onclick=( e => EditDangerConfirm( info.ID, info.DangerValue) ) >
    </div>
    
}

 

이런 식으로 썼는데 bridges를 순회하면서 html코드를 생성하고 

@onclick 이벤트에 연결 했는데... 

 

화면에서 테스트를 해보니... 무조건 마지막에 연결되었던 객체 정보로 뜬다. 

원인 ::

@onclick이 발생하는 때에는 데이타가 계속 덮어씌여져서 가장 마지막 요소를 가지고 오픈하니까 ... 엉뚱한게 보여진다. 

구글링을 해보니... 저렇게 쓰면 안된단다.. 생각해보니.. 그게 맞는 것도 같고..

 

해결방법 :: 

@foreach( var brd in bridges ) {
    MonitoringInfo info = Service.GetMonitoringInfo( brd );
	
    <div ... >
    	<button @onclick=( e => EditDangerConfirm( info.ID, info.DangerValue) ) >
    </div>
    
}

 

독립변수가 되겠끔. 

   MonitoringInfo info ; 선언을 foreach 안쪽으로 내렸다.

이렇게 하면 @onclick되는 시점에 바인딩될때의 변수값들을 가지고 컨펌창이 뜬다.

 

 

 

 

'# 4) .Net ( Vs 2010 ) > Web Matrix _Razor' 카테고리의 다른 글

Web Matrix] 구조화 컨텐츠... Layout  (0) 2011.04.20
Web Metrix ] 첫만남?  (0) 2011.04.19

# WCF 간단예제
 - ABC ( Address<where>, Binding<how>, Contract<what> )
 인터넷 찾아보면 많이 나오니까 ... 건너뛰고 코드로 정리해본다. 

 

# WCF 라이브러리 프로젝트

    . IASvc
    . IBSvc
    . WebSvc : IASvc, IBSvc

 

# Console 프로젝트
      . CustomIDPWDValidator : System.IdentityModel.Selectors.UserNamePasswordValidator 
          - 사용자 정의 로그인 인증 처리 클래스  ( mmc 에서 인증서 복사 ( 개인용 -> 신뢰할수 있는 사용자 ) )
      . Main()에 구현
      {
            서비스 구현
      }

 

# Window 서비스 : 추후에 윈도우 서비스에 올려 사용 

      . 서비스 protected override void OnStart(string[] args)
      {

            ServiceHost = new ( typeof(WCF라이브러리.클래스타입) );

            ServiceHost.Open();

      }

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
## windows 인증
 
[[ 서버 ]]
WSHttpBinding bind = new WSHttpBinding(SecurityMode.Message);
bind.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
Uri baseUri = new Uri("http://localhost:9633/Package");
 
using (ServiceHost host = new ServiceHost(typeof(WebSvc), baseUri))
{
    ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
    smb.HttpGetEnabled = true;
    host.Description.Behaviors.Add(smb);
 
    // http://localhost:9633/Package/SVC_A/
    host.AddServiceEndpoint(typeof(IASvc), bind, "SVC_A"); // SVC_A 적용.
    // http://localhost:9633/Package/SVC_B/
    host.AddServiceEndpoint(typeof(IBSvc), bind, "SVC_B"); // SVC_B 적용.
 
    Console.WriteLine("host Starting... Stop is press Any key!");
    host.Open();
 
    Console.ReadKey();
    host?.Close();
}
 
 
[[ 클라이언트 ]]
string url = "http://localhost:9633/Package/SVC_A/";
System.ServiceModel.WSHttpBinding binding = 
new System.ServiceModel.WSHttpBinding(System.ServiceModel.SecurityMode.Message);
binding.Security.Message.ClientCredentialType = 
System.ServiceModel.MessageCredentialType.Windows;
System.ServiceModel.EndpointAddress endPoint = 
new System.ServiceModel.EndpointAddress(url);
 
using (PackageService.ASvcClient clnt = new PackageService.ASvcClient(binding, endPoint))
{
    //같은 pc일때는 주석해도 동작! 다른 pc에서 접근시 대상pc의 계정, 비밀번호가 필요함.
    //clnt.ClientCredentials.Windows.ClientCredential = 
new System.Net.NetworkCredential("계정""비밀번호");
    MessageBox.Show(clnt.Hello_ServiceA());
}
// 서비스 B도 위와 동일함. 
 
 
 
 
 
## 사용자정의 인증처리. ( 운용시 X.509 인증서 필요 )
 
[[ 서버 ]]
WSHttpBinding bind = new WSHttpBinding(SecurityMode.Message);
bind.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
Uri baseUri = new Uri("http://localhost:9633/Package");
 
using (ServiceHost host = new ServiceHost(typeof(WebSvc), baseUri))
{
    ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
    smb.HttpGetEnabled = true;
    host.Description.Behaviors.Add(smb);
 
    host.Credentials.ServiceCertificate.SetCertificate(
            System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine,
            System.Security.Cryptography.X509Certificates.StoreName.My,
            System.Security.Cryptography.X509Certificates.X509FindType.FindBySubjectName,
"{등록한 인증서명}");
 
   host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = 
System.ServiceModel.Security.UserNamePasswordValidationMode.Custom;
   host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = 
new CustomIDPWDValidator(); // 사용자정의 id,pwd 인증
 
    // http://localhost:9633/Package/SVC_A/
    host.AddServiceEndpoint(typeof(IASvc), bind, "SVC_A"); // SVC_A 적용.
    // http://localhost:9633/Package/SVC_B/
    host.AddServiceEndpoint(typeof(IBSvc), bind, "SVC_B"); // SVC_B 적용.
 
    Console.WriteLine("host Staring... Stop is press Any key!");
    host.Open();
 
    Console.ReadKey();
    host?.Close();
}
 
 ** 테스트 인증서 발급
 (( 비주얼스튜디오에서 솔루션 트리 > 
프로젝트에서 우측버튼 > Open In Terminal 하면 파워쉘 뜨는데 아래 명령어 실행. ))
 
makecert.exe -sr LocalMachine -ss My -a sha1 -n CN={등록한 인증서명} -sky exchange -pe
 
makecert.exe -sr CurrentUser -ss My -a sha1 -n CN=CLNT -sky exchange -pe 
//클라이언트용인데... 실제 테스트 해보면 이것까지 할 필요는 없다.
 
참고 :
https://www.codeproject.com/Articles/36683/9-simple-steps-to-enable-X-509-certificates-on-WCF
 
 
[[ 클라이언트 ]]
string url = "http://localhost:9633/Package/SVC_A/";
System.ServiceModel.WSHttpBinding binding = 
new System.ServiceModel.WSHttpBinding(System.ServiceModel.SecurityMode.Message);
binding.Security.Message.ClientCredentialType = 
System.ServiceModel.MessageCredentialType.UserName;
System.ServiceModel.EndpointAddress endPoint = 
new System.ServiceModel.EndpointAddress(
    new Uri(url),
System.ServiceModel.EndpointIdentity.CreateDnsIdentity("{등록한 인증서명}")); 
//서버 쪽에 설정된 인증서명
 
using (System.ServiceModel.ChannelFactory<PackageService.IASvcChannel> proxy =
    new System.ServiceModel.ChannelFactory<PackageService.IASvcChannel>(binding, endPoint))
{
    /*MESSAGE, USERNAME 일때 로그인 인증*/
    proxy.Credentials.UserName.UserName = "로그인ID";  
//pc계정아님 - wcf통신 인증을 위한 로그인ID - 시스템 사용자id( 예:erp로그인 아이디 )
    proxy.Credentials.UserName.Password = "로그인PWD"
//pc계정아님 - wcf통신 인증을 위한 로그인PWD - 시스템 사용자id pwd( 예:erp로그인 아이디 pwd )
    proxy.Credentials.ServiceCertificate.Authentication.CertificateValidationMode =
        System.ServiceModel.Security.X509CertificateValidationMode.None;
    /*MESSAGE, USERNAME 일때 로그인 인증*/
 
    PackageService.IASvcChannel ch = proxy.CreateChannel();
    MessageBox.Show(ch?.Hello_ServiceA());
}
 
 
 
cs

 

사용자정의 로그인처리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class CustomIDPWDValidator : System.IdentityModel.Selectors.UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            if (userName == "로그인ID" && password == "로그인PWD")
            {                
                Console.WriteLine($"[{userName}] 로그인");
                return;
            }
            else
            {
                throw new System.IdentityModel.Tokens.SecurityTokenException("땡!");
            }
        }
    }
cs

 

서버 서비스쪽에서 클라이언트 ip 정보를 얻을때 아래 remote 객체에 .Address에 있음

1
2
3
4
5
OperationContext oc = OperationContext.Current;
System.ServiceModel.Channels.RemoteEndpointMessageProperty remote =
     oc.IncomingMessageProperties[System.ServiceModel.Channels.RemoteEndpointMessageProperty.Name]
         as System.ServiceModel.Channels.RemoteEndpointMessageProperty;
 
cs

 

서버에서 등록한 인증서명 = {등록한 인증서명} 을

클라이언트에서 EndpointAddress 설정시 url과 EndpointIdentity.CreateDnsIdentity("{등록한 인증서명}")으로

등록하여야 함.

만약 등록하지 않고 다른 코드처럼 하면 아래와 같은 에러가 뜸.

 System.ServiceModel.Security.MessageSecurityException

  HResult=0x80131501

  메시지=보내는 메시지의 ID를 검사하지 못했습니다. 원격 끝점의 필요한 DNS ID가 'localhost'이지만 원격 끝점이 DNS 클래임 '{등록한 인증서명}'을(를) 제공했습니다.

   이 끝점이 올바른 원격 끝점인 경우 채널 프록시를 만들 때 DNS ID '{등록한 인증서명}'을(를) EndpointAddress의 Identity 속성으로 명시적으로 지정하여 이 문제를 해결할 수 있습니다. 

 

서버에서 인증서 등록!! 확인

   1. 커맨드창에 mmc <- 실행.

   2. 실행후 파일 > 스냅인 추가/제거 클릭하면 아래처럼 팝업.

   3. 인증서 추가 > 인증서 스냅인 ( 내 사용자 계정 ) 으로 선택 후 마침.

   4. 테스트 인증서 복사 : 개인 인증서에서 >> 신뢰할수 있는 사용자 인증서로 복사!! 

      : 운영 일때는 조금 다르겠지?

mmc 실행창이 뜬 후 인증서 스냅인 추가.

mmc 스냅인 사용하여 등록... 

docs.microsoft.com/ko-kr/dotnet/framework/wcf/feature-details/how-to-view-certificates-with-the-mmc-snap-in

 

 

방법: MMC 스냅인을 사용 하 여 인증서 보기 - WCF

보안 WCF 클라이언트 또는 서비스는 인증서를 자격 증명으로 사용할 수 있습니다. MMC 플러그 인을 사용 하 여 검사할 수 있는 인증서 저장소의 유형에 대해 알아봅니다.

docs.microsoft.com

 

# 테스트 했을때

 

 

'# 4) .Net ( Vs 2010 ) > 차세대 WPF, WCF' 카테고리의 다른 글

Progress ] 로딩표시 창!  (0) 2015.10.15
WPF] Numeric TextBox  (0) 2015.09.15

 

 음... DB관련 툴을 만들어보다가 판넬에 스크롤이 있을때  작은 미리보기 창이 뜨면 괜찮겠다 싶어서...

보통의 다른 프로그램들은 있으니까...

 

한번 만들어보자 싶어 구현해봤다. 퇴근해서 2시간정도씩 해서 5시간정도 해본것 같은데...

 

쉽게생각했는데... 이상하게 끝이 안맞아서... 예외처리로 끝을 맞췄다.

 

 

 현재 스크롤을 따라서 우측에 작은 박스가 이동하게 되어있다.

우측 판넬 배경은 나중에 왼쪽 판넬 배경을 캡쳐해서 배치하면 좀 더 그럴듯 하게 만들수 있겠다.

 

음... 이런 잔 기술들을 따로 모아서 영상을 만들어볼까나... 문득.. 떠올랐다.

 

[ 소스는 첨부로 숨김 ]

 

 

 

 이번 플젝에서 그리드에 다음과 같이 표시해야 할 일이 있었다.

 

시작점은 노란색 바탕인데 A타입은 제일 아래, B타입은 그 한칸 위에서 시작한다.

 

시계방향으로 숫자를 배치하면 된다.

 

  테스트 2는  시작위치에서 부터 한칸씩 위로 올라가면서 값을 채우고

 끝까지 올라가면서 옆으로 옮겨서 내려오면서 값을 채우며 끝난다.

 

  private void test2()
        {
            DataTable table = new DataTable();
            table.Columns.Add("Block00");
            table.Columns.Add("Block01");

            int totalCount = 4;
            for (int loop = 0; loop < totalCount; loop++)
            {
                table.Rows.Add();
            }

            string type = "b";

            int currentCol = 0;
            int currentRow = (type == "a" ? totalCount : totalCount - 1 ) - 1;
            int startValue = 1;
            int step = -2;

            do
            {
                table.Rows[currentRow][currentCol] = startValue++;
                currentRow += step;               
                if (currentRow < 0)
                {
                    step *= -1; // 방향전환
                    currentRow = Math.Abs( currentRow - 1 ) % 2;
                    currentCol++;
                }
             
            } while (currentRow < totalCount && currentCol < 2); // 시계방향일때 종료지점
            dataGridView1.DataSource = table;
        }

 

 

 // 테스트는 위에서 부터 아래로 내려오면서 간단한 계산을 통해

 값을 채워 내려온다.  대신 타입과 홀수/짝수에 따라 시작위치가 다르다.

 노란색 시작점에서 시작이 아니고 제일 위에서부터 내려오면서 값을 채운다.

 

        private void testc()
        {
            DataTable table = new DataTable();
            table.Columns.Add("Block00");
            table.Columns.Add("Block01");

            int totalCount = 13;
            for (int loop = 0; loop < totalCount; loop++)
            {
                table.Rows.Add();
            }

            int startcol = 0;
            int startValue = totalCount / 2 + totalCount % 2;
            string type = "b";

            int simbol = 0;

            if (type == "a")
            {
                if (totalCount % 2 == 0) // 짝
                {
                    startcol = 1;
                    simbol = 1;
                    startValue ++; // a일때 짝수는 +1
                }
                else
                {
                    startcol = 0;
                    simbol = -1;
                }
            }
            else if (type == "b")
            {
                if (totalCount % 2 == 1) // 홀
                {
                    startcol = 1;
                    simbol = 1;
               
                }
                else
                {
                    startcol = 0;
                    simbol = -1;
                 
                }
            }

            for (int loop = 0; loop < totalCount; loop++)
            {               
                startValue = (simbol * loop) + startValue;
                simbol *= -1;
                table.Rows[loop]["Block0" + startcol] = startValue;
                startcol = ( startcol + 1 ) % 2;            
            }
            dataGridView1.DataSource = table;
        }

 

       이 방법은 간단한 수의 규칙을 찾아내고 만든 것이다ㅏ.

   

A타입만 써놨지만. B타입 4위치가 우측 칸부터 시작하고 우측에 표시되는 덧셈 뺄셈값의 부호가 반대가 된다. ( -1, +2, -3, +4, -5, 6 )

 

위의 두 처리방법 모두 간단한것인긴 한데..... 그냥 적어두자.

 

 

 음... 오늘 플젝하면서 그리드 Summary 구현하다가 하도 지저분해져서 좀 쉽게 안되나 하고

 

고민 고민 하다가 좋은걸 만들어냈다.

 

아래는 샘플로 구현했던 코드이다.

 

요새 상용 그리드는 밴드까지 있어서 컬럼 하나 찾아 설정하려면 코드가 많이 길어진다.

 

그래서 탄생한 로직이 아래처럼 만든 소스이다.

 

원래 하려던 소스는

    SettingColumns( 그리드, 밴드, ("컬럼키", ( col )=>{ 

           컬럼 설정....

    }),  

    ("컬럼키2", ( col )=>{ 

           컬럼2 설정....

    }));

 

이런식으로... 처리할수 없을까 였는데...

 

쉽게 될것 같은게 한시간은 머리빠개지게 고민하고 코딩하고 했던것 같다.

 

Action이나 Func 대리자를 이용하여 처리하면 될 것 같았는데... 직접은 안되고.

Tuple<string, Action<Column>> 으로 여러개 하려니 직접 생성도 해야되고... 번거롭더라

 

그래서...  두 메서드를 연결하는 메서드 형태를 고안해냈다.

 

 

위에 소스의 흐름은 간략하게 설명하면 다음과 같다.      

    1. GetCol 을 호출하면서 컬럼키를 넘겨주고

    2. GetCol에서 반환된 Action<DataGridView>의 대리자가 SettingsColumns에 setTransfers에 쌓인다. 

    3. 다음 SettingsColumns에서 foreach하면서 쌓인 대리자를 하나씩 호출하면서 그리드를 넘겨주면서 호출을 한다.

    4. 그러면 Action<DataGridView> 대리자 안에 구현된 setColumn의 대리자를 호출하면서 그리드에서 컬럼을 꺼내 외부로 전달하게 된다.

    5. 전달받은 column 객체는 GetCol( "AAA", Col =>   이부분으로 Col 객체로 넘어오게 된다.

   이렇게 순차적으로 넘어오면 컬럼을 각각 입맛에 맞춰 수정해주면 된다.

 

그래서 이걸 이대로 쓰나?? 아니다. 좀더 줄여본다.

 

 

동일한 동작을 하도록 대리자를 만들었다.

 

이렇게 하면 좋은점이라고 생각되는 것은 그리드의 컬럼을 설정할때 위와 같이 하면

 

컬럼1 설정...

 

컬럼2 설정...

 

컬럼3 설정...

...

 

이런식으로 세로로 쭉!! 나열했을때

컬럼에 다른 옵션등을 주면 소스가 중간이 두꺼워지고 빈줄로 구분하거나 region으로 구역을 나누게 되는데

 

위처럼 하면

그리드.SetColumns(

GetCol("컬럼키", col =>{

        // 설정

}),

GetCol("컬럼키2", col => {

        // 설정

}),

...

);

 

이런식으로 함수차제로 쪼갤 수 있다.

물론 다른 형태로 조각 조각 내서 관리할수 도 있다.

그래도 그리드설정시 기본 컬럼도 같이 한셋으로 묶는게 가능해졌다.

 

 요새 상용그리드 같은 경우 band개념이 있어서 소스가 지저분해져서... 어떻게하면 구역을 쪼개서 묶을수 있나... 라고 고민했는데...

여기에 딱이네. 

 

 

정말 오래 걸린것 같다. 마우스로 그리던 것을... 이번에 산 태블릿으로 프로그램올리니..

이상하게 그려지고 뚝뚝끊기고... 안찍히고... 등등... 이상이 많았는데...

 

이제 편하게 그릴수 있게 되었다. 데모 샘플도 만들었고, 인터넷에 없는 지우개 기능도 만들었고...

웃... 프로그램에서 그려진 것만 찍으니 위처럼 나왔다.  ( 역시 닷넷 좋아... 라이브러리 하나로 저렇게 구현이 가능하다니.. )

 

 

지우개도 구현이 되었다. ( 쉽지 않네 ..;; )

 

앞으로 할일은... 퇴근해서 Prezi와 Npt 그리고 Test이력 프로그램, 곧, 그림 그리는 기능들어간 것들은 모두 수정해야겠다.

 

이 맛에 코딩하는 것...

 

 

리소스... 관련해서 아래처럼 직접 관리가 가능하더랑...

( 이번에 사용할 프레임웍을 분석해보다가... 파일은 없는데 사용하는 코드만 있길래 구글링 열심히 해서 찾았다 )

ResourceWriter 로 리소스 Key, Value로 입력하면 아래 이미지 처럼 파일로 생성이 된다.

설명에는 개인정보등을 담아두지 말라고 써있다.

 

 

생성된 리소스 파일

>> 약간의 인코딩이 되어 있어 열면.. 깨진 텍스트 파일로 보인다.

 

> 소스파일은 숨겨둠.

 

 

 이번 프로젝트를 시작하면서 공통쪽을 준비하면서 다국어 관련 정보를 찾아봤다.

예전에 다국어 관련 테스트 했을때의 방법은 폼개발을 해놓고

 폼에서 Localization 프로퍼티 true 맞추고 Language를 변경하면 컨트롤에 리소스 파일이 하나 생긴다. 이를 이용해서 적용하는 방법이 있다.

 

이번에 찾은 방법은 좀 다르다. WPF처럼 리소스를 별도로 관리할수 있는 방법이다.

Dll_Resource 프로젝트를 만들고 여기에 각 언어별 리소스 파일을 만든다.

 

그리고 Demo_Resource는 Dll_Resource를 참조한다.

 

그리고 Dll_Resource는 Demo_Resource를 FriendAssembly 로 지정한다.

 

오잉??? 여기서 FriendAssembly 라는걸 처음 접했다.

 

Resouce의 접근자가 Internal로 지정되어 있는데 이를 public으로 변경 후 접근해도 되겠지만...

 

어셈블리끼리 친구먹게 해준다.

 

internal 접근자는 어셈블리 내에서 '공개' 제한자인데 어셈블리 밖에서 접근하려면 프로퍼티는 상속빼고는 접근이 안된다.

 

이걸 Dll_Resource 의 Assembly.cs에


// Assembly Frend
[assembly: InternalsVisibleTo("Demo_Resource")]

 

지정해주면!!! 접근이 가능하고

 

Demo_Resource의 소스코드 작성시

 

  apply(  Dll_Resource.Properties.Resources.ResourceManager  );

 

이렇게 접근이 가능하다.

 

>> 혼동하지 말기..

  리소스 접근한다고

      ResourceManager 객체로 로딩해서 접근하려고 하니

       자꾸 서명하라고 에러 뜨고 그런다. 서명을해도  공개키를 FriendAssembly 지정할때 적어주어야 한다는데...

          https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/assemblies-gac/how-to-create-signed-friend-assemblies

 

         음.... 근데 PublicKey 를??? 어떻게 얻지?.... 서명하고 키열어보면 ...

 

 

 

 

 

 

 

 

 코드트레인 보고 3D 랜더링 편 보며 따라 해봤는데

윈폼에 그리는 부분은 별도로 생성해서 작업

전에 별도로 공부할때보다 쉽게 만들어진 느낌??

아직 절두체 투영해서 뒤쪽 시야에 보이는 점들 숨기고 색칠하는 부분은 안해봤지만...

 

GIFMaker를 간만에 써보니 사용법을 까먹었네 ...

다시 폼화면에 화면 캡쳐기능있는 것을 써놓으려고 열었는데 이번엔 다른 문제가... 해결해놓고 기록해놨는데...

관련 라이브러리 사용했던 프로그램들이 같은 문제가 날것 같다. 해결은 간단하게 되는데...

윈10이 업데이트가 되서 그런건가...?

 

 

 

                          사이트에서 해본 것.

             이건 짧은 소스로 행렬곱 구현했던것으로 ...  결과 확인을 위해....

            이건 사이트처럼 값을 계산해서 채우는 형태로 구현한것... 

 다시 코드 트레인으로 돌아가보자... ( 3D 랜더링 ...  )