C# 기반 CNC 장비 연결 및 상태 수집 로직 구현

FOCAS 라이브러리 상태 코드 정의 및 오류 분기

CNC 제어기와 통신 시 반환되는 상태 코드를 명확히 정의하면 예외 처리 및 디버깅 효율성을 높일 수 있습니다. 기존 FOCASDLL에서 반환하는 오류 상수들을 C# 열거형(Enum)으로 구조화합니다.

public enum FocasReturnCode
{
    Success = 0,
    Busy = -1,
    ResetOrStopOccurred = -2,
    SystemError = -5,
    UnexpectedError = -6,
    VersionMismatch = -7,
    InvalidHandle = -8,
    HssbCommunicationError = -9,
    System2Error = -10,
    BusError = -11,
    MissingDll = -15,
    SocketError = -16,
    ProtocolError = -17,
    FunctionNotReady = 1,
    PmcNotPresent = 1,
    InvalidDataLength = 2,
    InvalidDataNumber = 3,
    AddressOutOfRange = 3,
    InvalidAttribute = 4,
    InvalidDataType = 4,
    InvalidDataValue = 5,
    NoOption = 6,
    WriteProtected = 7,
    MemoryOverflow = 8,
    InvalidCncParameter = 9,
    BufferError = 10,
    PathError = 11,
    InvalidMode = 12,
    ExecutionRejected = 13,
    DataServerError = 14,
    AlarmActive = 15,
    CncNotRunning = 16,
    PasswordProtected = 17
}

연결 안정성 모니터링 및 타임아웃 처리

장비 연결 직후 또는 통신 중 오류가 발생했을 때, 일정 시간 이상 비정상 상태가 지속될 경우 안전을 위해 해당 기계를 강제 대기 상태로 전환하는 로직이 필요합니다.

public async Task HandleConnectionDeteriorationAsync(
    long machineId, 
    FocasReturnCode currentCode, 
    DateTime lastSuccessfulPing)
{
    if (currentCode == FocasReturnCode.Success)
        return;

    var targetMachine = await _machineRepository.GetByIdAsync(machineId);
    if (targetMachine is null || targetMachine.CurrentState == MachineState.Stopped)
        return;

    TimeSpan elapsed = DateTime.UtcNow - lastSuccessfulPing;

    if (elapsed.TotalMinutes > 20)
    {
        await ForceTransitionToStoppedAsync(targetMachine);
        await _logService.WarnAsync($"장비 {machineId} 통신 불안정(20분 초과)으로 인해 강제 중단 처리됨.");
    }
}

장비 가동 구간 기록 관리

장비의 가동 및 정지 이력을 추적하기 위해 시작 시점과 종료 시점을 기록하는 테이블을 관리합니다. 현재 진행 중인 세션이 있다면 종료 시간으로 업데이트하고, 없을 경우 새로운 세션 기록을 생성합니다.

public async Task RecordOperationSessionAsync(long deviceId, DateTime timestamp)
{
    var activeRecord = await _sessionRepository
        .Query()
        .Where(r => r.DeviceId == deviceId && r.StartTime != null && r.EndTime == null)
        .OrderByDescending(r => r.RecordId)
        .FirstOrDefaultAsync();

    if (activeRecord != null)
    {
        activeRecord.EndTime = timestamp;
        activeRecord.LastModifiedBy = "SYSTEM_COLLECTOR";
        activeRecord.LastModifiedAt = timestamp;
        await _sessionRepository.UpdateAsync(activeRecord);
    }
    else
    {
        var newRecord = new MachineOperationLog
        {
            RecordId = _idGenerator.GenerateNext(),
            DeviceId = deviceId,
            StartTime = timestamp,
            EndTime = null,
            IsDeleted = false,
            CreatedBy = "SYSTEM_COLLECTOR",
            CreatedAt = timestamp,
            LastModifiedBy = "SYSTEM_COLLECTOR",
            LastModifiedAt = timestamp
        };
        await _sessionRepository.InsertAsync(newRecord);
    }
}

상태가 변경될 경우 기존 레코드를 종료 처리하고 신규 기록을 생성하는 방식으로 데이터 무결성을 유지합니다.

팬택(Fanuc) CNC 제어기 상태 판정 로직

장비 연결 성공 후 실시간 상태는 `cnc_statinfo` API를 통해 획득합니다. 상태 판정 규칙은 다음과 같습니다.

  • 알람(`alarm`) 값이 0이 아닌 경우: ALARM 상태
  • 알람이 없고, 동작 모드(`run`)가 3인 경우: WORKING 상태
  • 그 외의 경우: IDLESTATE 상태
public record CncMachineState(CncOperationMode Mode, int AlarmCode);

public enum CncOperationMode
{
    Idle = 0,
    Working = 3,
    Alarm = 5
}

public async Task<CncMachineState> EvaluateMachineStatusAsync(int handle)
{
    var statusInfo = new FocasStatInfo();
    var resultCode = FocasApi.GetMachineStatus(handle, ref statusInfo);

    if (resultCode != FocasReturnCode.Success)
        throw new InvalidOperationException($"상태 조회 실패: {resultCode}");

    int rawRunMode = statusInfo.RunMode;
    int alarmCode = statusInfo.AlarmCode;

    // 알람 우선 판정 및 상태 매핑
    var effectiveMode = alarmCode != 0 
        ? CncOperationMode.Alarm 
        : (CncOperationMode)rawRunMode;

    return new CncMachineState(effectiveMode, alarmCode);
}

private string TranslateToTextualState(CncOperationMode mode) => mode switch
{
    CncOperationMode.Working => "WORKING",
    CncOperationMode.Alarm => "ALARM",
    _ => "IDLESTATE"
};

실제 시스템에서는 판정된 상태 값과 알람 메시지를 기반으로 해당 장비 전용 알람 이력 테이블(`DvMachineryAlarm`)에 미해결 알람이 존재하는지 조회하여 중복 알림을 방지하거나 실시간 경로를 업데이트합니다. 필터링 조건으로 장비 고유 식별자, 알람 분류 코드(예: `23`), 종료 시점 미기록, 그리고 구체적인 알람 명칭을 결합하여 현재 유효한 경고 사항만 추려냅니다.

태그: C# FanucFOCAS CNCDataAcquisition IndustrialIoT StateManagement

9월 4일 18:17에 게시됨