진실. 감사해요. 나는 그것을 기억하지 못했다. 실제로 거의 즉시 실행되는 명령이 필요하기 때문에 문제를 완전히 해결하지 못합니다(이 방법을 사용하면 매초만 확인할 수 있지만 적어도 모든 틱은 확인할 수 없음). 그러나 실제로 임시 솔루션을 쉽게 구축할 수 있습니다. 그것을 사용.
OnTimer가 시스템 클록에 맞춰 가장 빠른 속도를 가지지만, 즉 15ms마다, 저는 1ms 간격으로 루핑하는 것을 선호합니다(루프에서 Sleep(1) 사용).
이 기능은 이 Expert Advisor 또는 표시기에 대해 1초 미만의 간격으로 타이머 이벤트가 생성되어야 함을 클라이언트 터미널에 나타냅니다.
boolEventSetMillisecondTimer( 정수밀리초// 밀리초 수 );
매개변수
밀리초
【인】 타이머 이벤트의 빈도를 정의하는 밀리초 수입니다.
반환된 값
성공적으로 실행되면 true를 반환하고 그렇지 않으면 false를 반환합니다. 에러 코드를 받기 위해서는 GetLastError() 함수를 호출해야 합니다.
메모
이 기능은 고해상도 타이머가 필요한경우를 위해 설계되었습니다 . 즉,타이머 이벤트는 초당 한 번보다 더 자주 수신되어야 합니다. 몇 초 주기의 기존 타이머로 충분하다면 EventSetTimer() 를 사용하십시오.
일반적으로 이 함수는 OnInit() 함수 또는 클래스 생성자 에서 호출해야 합니다. 타이머에서 오는 이벤트를 처리하기 위해서는 Expert Advisor나 인디케이터에 OnTimer() 함수가 있어야 합니다.
각 Expert Advisor와 각 표시기는 이 타이머에서만 이벤트를 수신하는 자체 타이머와 함께 작동합니다. mql4 애플리케이션 종료 중에 타이머가 생성되었지만 EventKillTimer() 함수에 의해 비활성화되지 않은 경우 타이머가 강제로 소멸됩니다.
각 프로그램에 대해 하나의 타이머만 시작할 수 있습니다. 각 mql4 애플리케이션 및 차트에는 새로 도착한 모든 이벤트가 배치되는 자체 이벤트 대기열이 있습니다. 대기열에 이미 Timer 이벤트가 포함되어 있거나 이 이벤트가 처리 단계에 있는 경우 새 Timer 이벤트는 mql4 애플리케이션 대기열에 추가되지 않습니다.
이 기능은 타이머 이벤트가 이 Expert Advisor 또는 표시기에 대해 1초 미만의 간격으로 생성되어야 함을 클라이언트 터미널에 나타냅니다.
boolEventSetMillisecondTimer( 정수밀리초// 밀리초 수 );
매개변수
밀리초
【인】 타이머 이벤트의 빈도를 정의하는 밀리초 수입니다.
반환된 값
성공적으로 실행되면 true를 반환하고 그렇지 않으면 false를 반환합니다. 에러 코드를 받기 위해서는 GetLastError() 함수를 호출해야 합니다.
메모
이 기능은 고해상도 타이머가 필요한 경우에 설계되었습니다 . 즉,타이머 이벤트는 초당 한 번보다 더 자주 수신되어야 합니다. 몇 초의 기간을 가진 기존 타이머로 충분하다면 EventSetTimer() 를 사용하십시오.
일반적으로 이 함수는 OnInit() 함수 또는 클래스 생성자 에서 호출해야 합니다. 타이머에서 오는 이벤트를 처리하기 위해서는 Expert Advisor나 인디케이터에 OnTimer() 함수가 있어야 합니다.
각 Expert Advisor와 각 표시기는 이 타이머에서만 이벤트를 수신하는 자체 타이머와 함께 작동합니다. mql4 애플리케이션 종료 중에 타이머가 생성되었지만 EventKillTimer() 함수에 의해 비활성화되지 않은 경우 타이머가 강제로 소멸됩니다.
각 프로그램에 대해 하나의 타이머만 시작할 수 있습니다. 각 mql4 애플리케이션 및 차트에는 새로 도착한 모든 이벤트가 배치되는 자체 이벤트 대기열이 있습니다. 대기열에 이미 Timer 이벤트가 포함되어 있거나 이 이벤트가 처리 단계에 있는 경우 새 Timer 이벤트가 mql4application 대기열에 추가되지 않습니다.
자세한 설명 감사합니다. 내 잘못; 온라인에서 너무 빨리 읽었습니다. (링크는 docs.mql4.com 도메인을 forum.mql4.com 과 교환했습니다. 다른 사람이 이 스레드를 확인할 경우를 대비하여...)
편집: 주위에 있는 중재자를 활용할 것입니다. 이전 게시물에서 설명한 대로 N 밀리초마다 OnTimer()를 실행하는 것과 N 밀리초마다 동일한 루프를 수행하는 것 사이에 차이점이 있는지 아십니까?
"ms" 타이밍으로 모든 소란을 피우는 이유를 모르겠습니다. 이것이 매우 낮은 대기 시간 중개자 연결에서 일종의 "고주파 뉴스 스캘퍼" 가 아닌 한, OnTimer() 의 1초 간격은 응답 시간으로 충분해야 합니다.
JAVA 앱 실행, IPC(LAN & WAN) 통신, 주문 관리 실행 및 브로커 서버 대기 시간의 모든 지연과 대기 시간을 추가하면 일반 전략에 "ms" 타이밍을 사용하는 데 실질적인 이점이 없습니다.
그러나 브로커에 대한 대기 시간이 짧은 VPS에서 사용되는 일종의 "High Frequency News Scalper" 인 경우 JAVA 및 IPC를 전혀 사용하지 않아야 하며 전략을 코딩해야 합니다. 가능한 가장 짧은 대기 시간을 얻기 위해 매우 컴팩트한 MQL에서 직접
편집하다:
별도의 데이터 피드와 함께 외부 앱을 사용하고 있다는 사실(MT4 피드와 관련하여 가격, 볼륨 및 타이밍에서 벗어나는 것이 보장됨)은 이렇게 빡빡한 "ms" 타이밍이 필요할 방법이 없음을 보여줍니다 . "1초" 폴링 간격이 적절합니다.
또한 OnTick() 이벤트는 들어오는 메시지를 확인할 수도 있으므로 "1초"는 들어오는 틱이 없을 때만 제한적인 경우입니다. 그리고 들어오는 틱이 없으면 어쨌든 그러한 빠른 "ms" 응답을 정당화하기에 충분한 활동(변동성 및 유동성)도 없습니다.
나는 비슷한 이유로 몇 년 전에 init()에서 시작된 루프에서 EA를 실행하곤 했습니다. 최소한의 지연으로 외부 데이터를 읽기 때문입니다. 부작용 없이 몇 주 동안 사용했습니다. Sleep(1)은 컴퓨팅 성능에 미치는 영향을 매우 가볍게 만들기에 충분합니다.
약간의 재미를 위해... 다음 스크립트는 여러 동시 TCP/IP 연결을 허용하고 들어오는 CR로 구분된 메시지를 Experts 로그에 씁니다. 예를 들어 스크립트가 실행되면 Telnet을 통해(기본적으로 포트 51234로) 연결할 수 있으며 입력한 텍스트의 각 행이 인쇄됩니다.
#property strict#property show_inputs
// ---------------------------------------------------------------------// User-configurable parameters// ---------------------------------------------------------------------inputint PortNumber = 51234 ; // TCP/IP port number// ---------------------------------------------------------------------// Constants// ---------------------------------------------------------------------// Size of temporary buffer used to read from client sockets#define SOCKET_READ_BUFFER_SIZE 10000// ---------------------------------------------------------------------// Forward definitions of classes// ---------------------------------------------------------------------// Wrapper around a connected client socketclass Connection;
// ---------------------------------------------------------------------// Winsock structure definitions and DLL imports// ---------------------------------------------------------------------struct sockaddr_in {
short af_family;
short port;
int addr;
int dummy1;
int dummy2;
};
struct timeval {
int secs;
int usecs;
};
struct fd_set {
int count;
int single_socket;
int dummy[ 63 ];
};
#import "Ws2_32.dll"int socket( int , int , int );
int bind( int , sockaddr_in&, int );
int htons( int );
int listen( int , int );
int accept( int , int , int );
int closesocket( int );
int select( int , fd_set&, int , int , timeval&);
int recv( int , uchar &[], int , int );
int WSAGetLastError();
#import
// ---------------------------------------------------------------------// Global variables// ---------------------------------------------------------------------// Handle of main listening server socketint ServerSocket;
// List of currently connected clients
Connection * Clients[];
// ---------------------------------------------------------------------// Entry point // ---------------------------------------------------------------------voidOnStart ()
{
if (! TerminalInfoInteger ( TERMINAL_DLLS_ALLOWED )) { Print ( "Requires \'Allow DLL imports\'" ); return ;}
// (Don't need to call WSAStartup because MT4 must have done this)// Create the main server socket
ServerSocket = socket( 2/* AF_INET */ , 1/* SOCK_STREAM */ , 6/* IPPROTO_TCP */ );
if (ServerSocket == - 1 ) { Print ( "ERROR " , WSAGetLastError() , " in socket creation" ); return ;}
// Bind the socket to the specified port number. In this example,// we only accept connections from localhost
sockaddr_in service;
service.af_family = 2/* AF_INET */ ;
service.addr = 0x100007F ; // equivalent to inet_addr("127.0.0.1")
service.port = ( short )htons(PortNumber);
if (bind(ServerSocket, service, 16/* sizeof(service) */ ) == - 1 ) { Print ( "ERROR " , WSAGetLastError() , " in socket bind" ); return ;}
// Put the socket into listening modeif (listen(ServerSocket, 0 ) == - 1 ) { Print ( "ERROR " , WSAGetLastError() , " in socket listen" ); return ;}
// Listening loop, which continues until Remove Script is used
timeval waitfor;
waitfor.secs = 0 ;
waitfor.usecs = 0 ;
while (! IsStopped ()) {
// .........................................................// Do we have a new pending connection on the server socket?
fd_set PollServerSocket;
PollServerSocket.count = 1 ;
PollServerSocket.single_socket = ServerSocket;
int selres = select( 0 , PollServerSocket, 0 , 0 , waitfor);
if (selres > 0 ) {
Print ( "New incoming connection..." );
int NewClientSocket = accept(ServerSocket, 0 , 0 );
if (NewClientSocket == - 1 ) {
Print ( "ERROR " , WSAGetLastError() , " in socket accept" );
} else {
Print ( "...accepted" );
int ctarr = ArraySize (Clients);
ArrayResize (Clients, ctarr + 1 );
Clients[ctarr] = new Connection(NewClientSocket);
Print ( "Got connection to client " , Clients[ctarr].GetID());
}
}
// .........................................................// Process any incoming data from client connections// (including any which have just been accepted, above)int ctarr = ArraySize (Clients);
for ( int i = ctarr - 1 ; i >= 0 ; i--) {
// Return value from ReadAnyPendingData() is true// if the socket still seems to be alive; false if // the connection seems to have been closed, and should be discardedif (Clients[i].ReadAnyPendingData()) {
// Socket still seems to be alive
} else {
// Socket appears to be dead. Delete, and remove from listPrint ( "Lost connection to client " , Clients[i].GetID());
delete Clients[i];
for ( int j = i + 1 ; j < ctarr; j++) {
Clients[j - 1 ] = Clients[j];
}
ctarr--;
ArrayResize (Clients, ctarr);
}
}
Sleep ( 10 ); // Sleep(1) appears to be a little too aggressive in this context
}
}
// ---------------------------------------------------------------------// Termination (could do this at the end of OnStart() instead.// It's just a little clearer to do it here// ---------------------------------------------------------------------voidOnDeinit ( constint reason)
{
closesocket(ServerSocket);
for ( int i = 0 ; i < ArraySize (Clients); i++) {
delete Clients[i];
}
}
// ---------------------------------------------------------------------// Simple wrapper around each connected client socket// ---------------------------------------------------------------------class Connection {
private :
// Client socket handleint mSocket;
// Temporary buffer used to handle incoming datauchar mTempBuffer[SOCKET_READ_BUFFER_SIZE];
// Stored-up data, waiting for a \r character string mPendingData;
public :
Connection( int ClientSocket) {mSocket = ClientSocket; mPendingData = "" ;}
~Connection() {closesocket(mSocket);}
string GetID() { returnIntegerToString (mSocket);}
bool ReadAnyPendingData();
};
// Called repeatedly on a timer from OnStart(), to check whether any// data is available on this client connection. Returns true if the // client still seems to be connected (*not* if there's new data); // returns false if the connection seems to be dead. bool Connection::ReadAnyPendingData()
{
// Check the client socket for data-readability
timeval waitfor;
waitfor.secs = 0 ;
waitfor.usecs = 0 ;
fd_set PollClientSocket;
PollClientSocket.count = 1 ;
PollClientSocket.single_socket = mSocket;
int selres = select( 0 , PollClientSocket, 0 , 0 , waitfor);
if (selres > 0 ) {
// Winsock says that there is data waiting to be read on this socketint res = recv(mSocket, mTempBuffer, SOCKET_READ_BUFFER_SIZE, 0 );
if (res > 0 ) {
// Convert the buffer to a string, and add it to any pending// data which we already have on this connectionstring strIncoming = CharArrayToString (mTempBuffer, 0 , res);
mPendingData += strIncoming;
// Do we have a complete message (or more than one) ending in \r?int idxTerm = StringFind (mPendingData, "\r" );
while (idxTerm >= 0 ) {
if (idxTerm > 0 ) {
string strMsg = StringSubstr (mPendingData, 0 , idxTerm);
// Print the \r-terminated message in the logPrint ( "#" , GetID() , ": " , strMsg);
}
// Keep looping until we have extracted all the \r delimited // messages, and leave any residue in the pending data
mPendingData = StringSubstr (mPendingData, idxTerm + 1 );
idxTerm = StringFind (mPendingData, "\r" );
}
returntrue ;
} else {
// recv() failed. Assume socket is deadreturnfalse ;
}
} elseif (selres == - 1 ) {
// Assume socket is deadreturnfalse ;
} else {
// No pending datareturntrue ;
}
}
약간의 재미를 위해... 다음 스크립트는 여러 동시 TCP/IP 연결을 허용하고 들어오는 CR로 구분된 메시지를 Experts 로그에 씁니다.
... 스크립트가 아닌 EA에서 위의 작업을 수행하려면 다음과 같이 변경하면 됩니다.
모든 코드를 OnStart()에서 listen() 호출까지 포함하여 EA의 OnInit()로 이동합니다.
OnInit() 끝에 고주파 타이머를 설정합니다(예: EventSetMillisecondTimer(10)).
나머지 코드를 OnStart()에서 OnTimer()로 이동하고 외부 "while (!IsStopped()) {}" 루프를 제거합니다.
최근 MT4 빌드에서 이 문제가 수정되지 않은 한 MT4 플랫폼이 이미 차트에 첨부된 EA로 시작될 때 OnInit()에서 사용하면 EventSetTimer() 및 EventSetMillisecondTimer()가 실패할 수 있다는 메모를 추가하겠습니다. 내 경험상 반환 값을 확인 하는 것이 필요합니다. 다시 해 보다; OnInit()에서 타이머를 생성하려는 시도가 실패한 경우 첫 번째 OnTick()에서 타이머를 설정하는 것으로 잠재적으로 폴백합니다.
진실. 감사해요. 나는 그것을 기억하지 못했다. 실제로 거의 즉시 실행되는 명령이 필요하기 때문에 문제를 완전히 해결하지 못합니다(이 방법을 사용하면 매초만 확인할 수 있지만 적어도 모든 틱은 확인할 수 없음). 그러나 실제로 임시 솔루션을 쉽게 구축할 수 있습니다. 그것을 사용.
OnTimer가 시스템 클록에 맞춰 가장 빠른 속도를 가지지만, 즉 15ms마다, 저는 1ms 간격으로 루핑하는 것을 선호합니다(루프에서 Sleep(1) 사용).
OnTimer가 시스템 클록에 맞춰 가장 빠른 속도를 가지지만, 즉 15ms마다, 저는 1ms 간격으로 루핑하는 것을 선호합니다(루프에서 Sleep(1) 사용).
문제가 되지 않을까요? 내 말은, MT4에는 OnTick()을 실행하여 중지하고 반복하지 않고 몇 주 동안 실행(EA)한 후 오버플로되거나 크기가 초과되는 일종의 내부 버퍼를 방지하기 위해 실행해야 할 수 있는 내부 프로세스가 없습니다.
무지해서 물어보는건데 MT4가 그렇게 하도록 고안되지 않았다고 가정하면 문제가 될 수 있는지 모르겠습니다.
그래서 당신은 그것이 문제가 되지 않을 것이라고 생각합니까? (그렇다면 좋은 소식입니다.)
BTW: OnTimer() 가 1초 간격 미만으로 실행되도록 하려면 어떻게 합니까? :?
나는 비슷한 이유로 몇 년 전에 init()에서 시작된 루프에서 EA를 실행하곤 했습니다. 최소한의 지연으로 외부 데이터를 읽기 때문입니다. 부작용 없이 몇 주 동안 사용했습니다. Sleep(1)은 컴퓨팅 성능 에 미치는 영향을 매우 가볍게 만들기에 충분합니다.
타이머 설정은 밀리 단위를 허용합니다.
나는 비슷한 이유로 몇 년 전에 init()에서 시작된 루프에서 EA를 실행하곤 했습니다. 최소한의 지연으로 외부 데이터를 읽기 때문입니다. 부작용 없이 몇 주 동안 사용했습니다. Sleep(1)은 컴퓨팅 성능에 미치는 영향을 매우 가볍게 만들기에 충분합니다.
타이머 설정은 밀리 단위를 허용합니다.
BTW: OnTimer()가 1초 간격 미만으로 실행되도록 하려면 어떻게 합니까? :?
EventSetMillisecondTimer
이 기능은 이 Expert Advisor 또는 표시기에 대해 1초 미만의 간격으로 타이머 이벤트가 생성되어야 함을 클라이언트 터미널에 나타냅니다.
bool EventSetMillisecondTimer (
정수 밀리초 // 밀리초 수
);
매개변수
밀리초
【인】 타이머 이벤트의 빈도를 정의하는 밀리초 수입니다.
반환된 값
성공적으로 실행되면 true를 반환하고 그렇지 않으면 false를 반환합니다. 에러 코드를 받기 위해서는 GetLastError() 함수를 호출해야 합니다.
메모
이 기능은 고해상도 타이머가 필요한 경우를 위해 설계되었습니다 . 즉, 타이머 이벤트는 초당 한 번보다 더 자주 수신되어야 합니다. 몇 초 주기의 기존 타이머로 충분하다면 EventSetTimer() 를 사용하십시오.
일반적으로 이 함수는 OnInit() 함수 또는 클래스 생성자 에서 호출해야 합니다. 타이머에서 오는 이벤트를 처리하기 위해서는 Expert Advisor나 인디케이터에 OnTimer() 함수가 있어야 합니다.
각 Expert Advisor와 각 표시기는 이 타이머에서만 이벤트를 수신하는 자체 타이머와 함께 작동합니다. mql4 애플리케이션 종료 중에 타이머가 생성되었지만 EventKillTimer() 함수에 의해 비활성화되지 않은 경우 타이머가 강제로 소멸됩니다.
각 프로그램에 대해 하나의 타이머만 시작할 수 있습니다. 각 mql4 애플리케이션 및 차트에는 새로 도착한 모든 이벤트가 배치되는 자체 이벤트 대기열이 있습니다. 대기열에 이미 Timer 이벤트가 포함되어 있거나 이 이벤트가 처리 단계에 있는 경우 새 Timer 이벤트는 mql4 애플리케이션 대기열에 추가되지 않습니다.
EventSetMillisecondTimer
이 기능은 타이머 이벤트가 이 Expert Advisor 또는 표시기에 대해 1초 미만의 간격으로 생성되어야 함을 클라이언트 터미널에 나타냅니다.
bool EventSetMillisecondTimer (
정수 밀리초 // 밀리초 수
);
매개변수
밀리초
【인】 타이머 이벤트의 빈도를 정의하는 밀리초 수입니다.
반환된 값
성공적으로 실행되면 true를 반환하고 그렇지 않으면 false를 반환합니다. 에러 코드를 받기 위해서는 GetLastError() 함수를 호출해야 합니다.
메모
이 기능은 고해상도 타이머 가 필요한 경우에 설계되었습니다 . 즉, 타이머 이벤트는 초당 한 번보다 더 자주 수신되어야 합니다. 몇 초의 기간을 가진 기존 타이머로 충분하다면 EventSetTimer() 를 사용하십시오.
일반적으로 이 함수는 OnInit() 함수 또는 클래스 생성자 에서 호출해야 합니다. 타이머에서 오는 이벤트를 처리하기 위해서는 Expert Advisor나 인디케이터에 OnTimer() 함수가 있어야 합니다.
각 Expert Advisor와 각 표시기는 이 타이머에서만 이벤트를 수신하는 자체 타이머와 함께 작동합니다. mql4 애플리케이션 종료 중에 타이머가 생성되었지만 EventKillTimer() 함수에 의해 비활성화되지 않은 경우 타이머가 강제로 소멸됩니다.
각 프로그램에 대해 하나의 타이머만 시작할 수 있습니다. 각 mql4 애플리케이션 및 차트에는 새로 도착한 모든 이벤트가 배치되는 자체 이벤트 대기열이 있습니다. 대기열에 이미 Timer 이벤트가 포함되어 있거나 이 이벤트가 처리 단계에 있는 경우 새 Timer 이벤트가 mql4application 대기열에 추가되지 않습니다.
자세한 설명 감사합니다. 내 잘못; 온라인에서 너무 빨리 읽었습니다. (링크는 docs.mql4.com 도메인을 forum.mql4.com 과 교환했습니다. 다른 사람이 이 스레드를 확인할 경우를 대비하여...)
편집: 주위에 있는 중재자를 활용할 것입니다. 이전 게시물에서 설명한 대로 N 밀리초마다 OnTimer()를 실행하는 것과 N 밀리초마다 동일한 루프를 수행하는 것 사이에 차이점이 있는지 아십니까?
"ms" 타이밍으로 모든 소란을 피우는 이유를 모르겠습니다. 이것이 매우 낮은 대기 시간 중개자 연결에서 일종의 "고주파 뉴스 스캘퍼" 가 아닌 한, OnTimer() 의 1초 간격은 응답 시간으로 충분해야 합니다.
JAVA 앱 실행, IPC(LAN & WAN) 통신, 주문 관리 실행 및 브로커 서버 대기 시간의 모든 지연과 대기 시간을 추가하면 일반 전략에 "ms" 타이밍을 사용하는 데 실질적인 이점이 없습니다.
그러나 브로커에 대한 대기 시간이 짧은 VPS에서 사용되는 일종의 "High Frequency News Scalper" 인 경우 JAVA 및 IPC를 전혀 사용하지 않아야 하며 전략을 코딩해야 합니다. 가능한 가장 짧은 대기 시간을 얻기 위해 매우 컴팩트한 MQL에서 직접
편집하다:
편집: 주위에 있는 중재자를 활용할 것입니다. 이전 게시물에서 설명한 대로 N 밀리초마다 OnTimer()를 실행하는 것과 N 밀리초마다 동일한 루프를 수행하는 것 사이에 차이점이 있는지 아십니까?
내가 중재자라고 해서 내가 다른 사람들보다 더 지식이 있다는 것을 의미하지는 않습니다. 사실, 이 스레드의 다른 포스터들은 분명히 그 주제에 대해 나보다 더 많은 지식을 가지고 있습니다. 나는 자바에 대해 아무것도 모른다.
타이머 이벤트에서 Sleep()을 사용 하는 요점을 이해하지 못합니다. EA가 보류되고 이는 OnTick() 이벤트가 많이 누락됨을 의미할 수 있기 때문입니다. 물론 EA에 따라 OnTick 이벤트는 중요하지 않을 수 있습니다.
나는 비슷한 이유로 몇 년 전에 init()에서 시작된 루프에서 EA를 실행하곤 했습니다. 최소한의 지연으로 외부 데이터를 읽기 때문입니다. 부작용 없이 몇 주 동안 사용했습니다. Sleep(1)은 컴퓨팅 성능에 미치는 영향을 매우 가볍게 만들기에 충분합니다.
약간의 재미를 위해... 다음 스크립트는 여러 동시 TCP/IP 연결을 허용하고 들어오는 CR로 구분된 메시지를 Experts 로그에 씁니다. 예를 들어 스크립트가 실행되면 Telnet을 통해(기본적으로 포트 51234로) 연결할 수 있으며 입력한 텍스트의 각 행이 인쇄됩니다.
약간의 재미를 위해... 다음 스크립트는 여러 동시 TCP/IP 연결을 허용하고 들어오는 CR로 구분된 메시지를 Experts 로그에 씁니다.
... 스크립트가 아닌 EA에서 위의 작업을 수행하려면 다음과 같이 변경하면 됩니다.