IVariable
지속성, 변경 추적 및 범위 관리 기능을 갖춘 타입이 지정된 값을 저장하는 액터 변수를 위한 인터페이스입니다. 변수는 액터 상태, 구성 매개변수 및 런타임 데이터를 저장하는 주요 메커니즘입니다.
네임스페이스: ControlBee.Interfaces
상속: IActorItem, INotifyValueChanged
개요
ControlBee의 변수는 다음과 같은 여러 주요 기능을 갖춘 강력한 타입의 범위 인식 상태 관리를 제공합니다:
- 타입 안전성 - 제네릭
Variable<T>는 컴파일 타임 타입 검사를 보장합니다 - 범위 지정된 지속성 - Global, Local 및 Temporary 범위가 저장/로드 동작을 제어합니다
- 변경 추적 - 값이 변경될 때 자동 알림
- 감사 추적 - 누가 언제 값을 수정했는지 추적
- 레시피 관리 - 다양한 제품에 대한 변수 세트 저장/로드
- UI 통합 - 구성 인터페이스에 자동으로 표시
변수는 일반적으로 액터 클래스의 public 필드로 선언됩니다:
public class StageActor : Actor
{
// Global - 모든 레시피에서 공유, 자동 저장
public Variable<double> HomeSpeed = new(VariableScope.Global, 10.0);
public Variable<double> SafetyMargin = new(VariableScope.Global, 5.0);
// Local - 레시피별로 다른 값, 자동 저장
public Variable<double> Speed = new(VariableScope.Local, 100.0);
public Variable<Position1D> LoadPos = new(VariableScope.Local);
// Temporary - 런타임 상태, 자동 저장 안 됨
public Variable<bool> Busy = new(VariableScope.Temporary);
public Variable<int> CurrentIndex = new(VariableScope.Temporary);
}
속성
Id
int? Id { get; }
변수가 저장될 때 할당되는 이 변수의 데이터베이스 ID입니다. 아직 저장되지 않았거나 Local/Temporary 범위 변수의 경우 null입니다.
사용:
- 변수 관리자의 내부 사용
- 지속성 상태 추적
- 데이터베이스 작업
ValueObject
object? ValueObject { get; set; }
타입이 지정되지 않은 객체로서의 변수의 현재 값입니다. 타입 안전 액세스를 위해서는 대신 제네릭 Variable<T>.Value 속성을 사용하세요.
사용 예시:
// 타입 안전 액세스 (권장)
Variable<double> speed = new(VariableScope.Local, 100.0);
double currentSpeed = speed.Value;
speed.Value = 150.0;
// 인터페이스를 통한 타입이 지정되지 않은 액세스 (제네릭 코드용)
IVariable variable = speed;
object? value = variable.ValueObject; // 150.0을 object로 반환
variable.ValueObject = 200.0; // 캐스팅 필요
사용 사례:
- 제네릭 변수 처리 코드
- 레시피 로드/저장
- UI 데이터 바인딩
- 리플렉션 기반 작업
OldValueObject
object? OldValueObject { get; }
마지막 변경 전의 이전 값입니다. 변경 감지, 유효성 검사 및 실행 취소 작업에 사용됩니다.
사용 예시:
public class StageActor : Actor
{
public Variable<double> Speed = new(VariableScope.Local, 100.0);
public StageActor(ActorConfig config) : base(config)
{
Speed.ValueChanged += (s, e) =>
{
double oldSpeed = (double)e.OldValue!;
double newSpeed = (double)e.NewValue!;
Console.WriteLine($"속도가 {oldSpeed}에서 {newSpeed}로 변경되었습니다");
if (newSpeed > 200)
{
// 이전 값으로 되돌리기
Speed.Value = oldSpeed;
Console.WriteLine("속도 제한 초과, 되돌림");
}
};
}
}
사용 사례:
- 변경 유효성 검사
- 실행 취소 기능
- 감사 로깅
- 변경 비교
Scope
VariableScope Scope { get; }
변수의 지속성 및 수명 주기 동작을 정의합니다:
VariableScope.Local- 레시피 구분 없이 데이터베이스에 저장됩니다 (모든 레시피에서 동일한 값). 제품 간에 변경되지 않는 시스템 전체 설정에 사용됩니다.VariableScope.Local- 레시피별로 데이터베이스에 저장됩니다 (레시피마다 다른 값). 제품별 구성에 사용됩니다.VariableScope.Temporary- 런타임 상태로 자동 저장되지 않습니다.SaveTemporaryVariables()로 수동 저장할 수 있으며, 전역적으로 저장됩니다 (레시피 구분 없음).
올바른 범위 선택:
// Global - 모든 레시피에서 공유되는 시스템 설정
public Variable<double> HomeSpeed = new(VariableScope.Local, 10.0);
public Variable<double> SafetyOffset = new(VariableScope.Local, 5.0);
public Variable<int> DebounceMs = new(VariableScope.Local, 50);
// Local - 레시피별로 변경되는 제품별 설정
public Variable<double> ProcessSpeed = new(VariableScope.Local, 100.0);
public Variable<Position1D> LoadPos = new(VariableScope.Local);
public Variable<int> GridRows = new(VariableScope.Local, 5);
// Temporary - 자주 변경되는 런타임 상태
public Variable<bool> Busy = new(VariableScope.Temporary);
public Variable<int> CurrentIndex = new(VariableScope.Temporary);
public Variable<GridContainer> WorkTray = new(VariableScope.Temporary);
참고:
ActorName
string ActorName { get; }
이 변수를 소유한 액터의 이름입니다. 다음에 사용됩니다:
- 데이터베이스 저장 - 액터별로 변수 구성
- 변경 기록 - 어떤 액터의 변수가 변경되었는지 추적
- UI 구성 - 액터별로 변수 그룹화
- 진단 - 변수 소유권 식별
예시:
var variable = actor.GetItem("Speed") as IVariable;
Console.WriteLine($"변수 소유자: {variable.ActorName}");
// 출력: 변수 소유자: Stage0
Dirty
bool Dirty { get; set; }
변수에 저장되지 않은 변경 사항이 있는지 여부를 나타냅니다. 값이 변경되면 true로 설정되고, 데이터베이스에 저장되면 지워집니다.
사용 예시:
// 저장되지 않은 변경 사항이 있는 변수 확인
foreach (var actor in actorRegistry.GetAll())
{
var items = actor.GetItems();
foreach (var (path, type) in items)
{
if (actor.GetItem(path) is IVariable variable && variable.Dirty)
{
Console.WriteLine($"{actor.Name}.{path}에 저장되지 않은 변경 사항이 있습니다");
}
}
}
// 모든 더티 변수 저장
variableManager.Save(); // 저장된 모든 변수의 Dirty 플래그 지우기
자동 추적:
Global및Local범위 변수의 경우 값이 변경되면Dirty가 자동으로true로 설정됩니다Temporary범위 변수의 경우Dirty는 자동 설정되지 않습니다 (수동 저장용)
사용 사례:
- 저장 시기 결정
- 저장되지 않은 변경 사항에 대한 경고
- UI의 변경 표시기
- 감사 추적 트리거
UserInfo
IUserInfo? UserInfo { get; set; }
이 변수를 마지막으로 수정한 사용자에 대한 정보입니다. 감사 추적 및 변경 추적에 사용됩니다.
속성:
UserName- 사용자의 로그인 이름AuthorityLevel- 사용자의 권한 레벨Timestamp- 변경이 이루어진 시간
사용 예시:
public class StageActor : Actor
{
public Variable<Position1D> LoadPos = new(VariableScope.Local);
public void TeachLoadPosition()
{
LoadPos.Value.TeachCurrent();
// UserInfo는 시스템에 의해 자동으로 설정됩니다
if (LoadPos.UserInfo != null)
{
Console.WriteLine($"위치를 티칭한 사용자: {LoadPos.UserInfo.UserName}");
Console.WriteLine($"시간: {LoadPos.UserInfo.Timestamp}");
}
}
}
// 변경 기록 조회
var changes = variableManager.ReadVariableChanges(new QueryOptions
{
Filter = "ActorName = 'Stage0' AND ItemPath = 'LoadPos'",
OrderBy = "Timestamp DESC",
Limit = 10
});
foreach (var change in changes)
{
Console.WriteLine($"{change.Timestamp}: {change.UserName}이(가) " +
$"{change.OldValue}에서 {change.NewValue}로 변경");
}
사용 사례:
- 감사 로깅
- 변경 책임성
- 규정 준수 요구 사항
- 디버깅(누가 무엇을 변경했는지)
메서드
ToJson
저장 또는 전송을 위해 변수 값을 JSON 문자열로 직렬화합니다.
string ToJson();
반환값: 현재 값의 JSON 표현입니다.
사용 예시:
// 단순 타입
Variable<double> speed = new(VariableScope.Local, 100.0);
string json = speed.ToJson();
Console.WriteLine(json); // 출력: "100.0"
// 복잡한 타입
Variable<Position1D> pos = new(VariableScope.Local);
pos.Value.Set(100, 50, 200, 100); // position, speed, accel, decel
string json = pos.ToJson();
// 출력: {"Position":100,"Speed":50,"Accel":200,"Decel":100}
// 배열
Variable<Array2D<bool>> grid = new(VariableScope.Local, new Array2D<bool>(3, 3));
grid.Value[1, 1] = true;
string json = grid.ToJson();
// 출력: [[false,false,false],[false,true,false],[false,false,false]]
사용 사례:
- 레시피 저장 - 변수를 데이터베이스에 저장
- 내보내기/가져오기 - 시스템 간 구성 전송
- 백업/복원 - 변수 상태 저장
- 네트워크 전송 - 원격 시스템으로 값 전송
FromJson
JSON 문자열에서 변수 값을 역직렬화합니다.
void FromJson(string data);
매개변수:
data— 역직렬화하여 변수에 할당할 JSON 문자열입니다.
사용 예시:
// 저장된 JSON에서 복원
Variable<double> speed = new(VariableScope.Local, 100.0);
speed.FromJson("150.0");
Console.WriteLine(speed.Value); // 출력: 150.0
// 복잡한 타입 로드
Variable<Position1D> pos = new(VariableScope.Local);
pos.FromJson("{\"Position\":100,\"Speed\":50,\"Accel\":200,\"Decel\":100}");
Console.WriteLine($"위치: {pos.Value.Position}"); // 출력: 위치: 100
// 배열 로드
Variable<Array2D<bool>> grid = new(VariableScope.Local, new Array2D<bool>(3, 3));
grid.FromJson("[[false,false,false],[false,true,false],[false,false,false]]");
Console.WriteLine($"중앙 셀: {grid.Value[1, 1]}"); // 출력: 중앙 셀: True
사용 사례:
- 레시피 로드 - 데이터베이스에서 변수 복원
- 가져오기 - 파일에서 구성 로드
- 네트워크 수신 - 원격 시스템에서 값 수신
- 실행 취소/다시 실행 - 이전 상태 복원
오류 처리:
try
{
variable.FromJson(jsonString);
}
catch (JsonException ex)
{
Console.WriteLine($"역직렬화 실패: {ex.Message}");
// 오류 처리 - 현재 값을 유지하거나 기본값 사용
}
변경 알림
변수는 INotifyValueChanged를 구현하여 구독자가 값 변경 시 알림을 받을 수 있도록 합니다:
public class StageActor : Actor
{
public Variable<int> RowCount = new(VariableScope.Local, 5);
public Variable<int> ColCount = new(VariableScope.Local, 8);
public Variable<GridContainer> Tray = new(VariableScope.Temporary);
public StageActor(ActorConfig config) : base(config)
{
// 변경 사항 구독
RowCount.ValueChanged += OnLayoutChanged;
ColCount.ValueChanged += OnLayoutChanged;
}
private void OnLayoutChanged(object? sender, ValueChangedArgs e)
{
// 치수 변경 시 그리드 재생성
Tray.Value = new GridContainer(RowCount.Value, ColCount.Value);
Console.WriteLine($"그리드 크기가 {RowCount.Value}x{ColCount.Value}로 변경되었습니다");
}
}
참고:
일반적인 패턴
1. 변경 시 유효성 검사
public Variable<double> Speed = new(VariableScope.Local, 100.0);
public MyActor(ActorConfig config) : base(config)
{
Speed.ValueChanged += (s, e) =>
{
double newSpeed = (double)e.NewValue!;
if (newSpeed < 0 || newSpeed > 200)
{
Speed.Value = (double)e.OldValue!; // 되돌리기
ErrorDialog.Show("속도는 0에서 200 사이여야 합니다");
}
};
}
2. 계단식 업데이트
public Variable<bool> UseVacuum = new(VariableScope.Local, true);
public Variable<double> VacuumPressure = new(VariableScope.Local, -80.0);
public MyActor(ActorConfig config) : base(config)
{
UseVacuum.ValueChanged += (s, e) =>
{
// 진공을 사용하지 않을 때 진공 압력 숨기기
((IActorItemModifier)VacuumPressure).Visible = (bool)e.NewValue!;
};
}
3. 파생 상태
public Variable<int> RowCount = new(VariableScope.Local, 5);
public Variable<int> ColCount = new(VariableScope.Local, 8);
public Variable<int> TotalCells = new(VariableScope.Local, 40);
public MyActor(ActorConfig config) : base(config)
{
void UpdateTotal(object? s, ValueChangedArgs e)
{
TotalCells.Value = RowCount.Value * ColCount.Value;
}
RowCount.ValueChanged += UpdateTotal;
ColCount.ValueChanged += UpdateTotal;
}
제네릭 Variable 클래스
실제로는 IVariable을 구현하는 제네릭 Variable<T> 클래스를 사용합니다:
// 스칼라 타입
public Variable<int> Count = new(VariableScope.Local, 0);
public Variable<double> Value = new(VariableScope.Local, 0.0);
public Variable<bool> Flag = new(VariableScope.Local, false);
public Variable<string> Name = new(VariableScope.Local, "");
// 위치 타입
public Variable<Position1D> TargetX = new(VariableScope.Local);
public Variable<Position2D> TargetXY = new(VariableScope.Local);
public Variable<Position3D> TargetXYZ = new(VariableScope.Local);
// 배열 타입
public Variable<Array1D<bool>> Flags = new(VariableScope.Local, new Array1D<bool>(10));
public Variable<Array2D<int>> Grid = new(VariableScope.Local, new Array2D<int>(5, 8));
// 사용자 정의 타입
public Variable<GridContainer> Tray = new(VariableScope.Temporary);
참고
- 변수 시스템 가이드 - 변수 사용 완벽 가이드
IVariableManager- 액터 간 변수 관리INotifyValueChanged- 변경 알림 인터페이스IActorItem- 액터 항목의 기본 인터페이스