2025-06-27 17:50:23 +09:00
|
|
|
|
#nullable enable
|
|
|
|
|
|
|
|
|
|
|
|
using SampleProject;
|
|
|
|
|
|
using System;
|
2025-06-23 20:06:15 +09:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using UnityEngine;
|
2025-06-27 17:50:23 +09:00
|
|
|
|
using UVC.Core;
|
|
|
|
|
|
using UVC.Data;
|
|
|
|
|
|
using UVC.Extention;
|
2025-06-23 20:06:15 +09:00
|
|
|
|
using UVC.Factory.Component;
|
2025-07-01 20:10:15 +09:00
|
|
|
|
using UVC.Util;
|
2025-06-23 20:06:15 +09:00
|
|
|
|
|
|
|
|
|
|
namespace UVC.Factory.Alarm
|
|
|
|
|
|
{
|
2025-06-27 17:50:23 +09:00
|
|
|
|
public class AlarmManager : SingletonScene<AlarmManager>
|
2025-06-23 20:06:15 +09:00
|
|
|
|
{
|
2025-06-27 17:50:23 +09:00
|
|
|
|
[Tooltip("알람 UI 프리팹입니다. 이 프리팹은 알람 정보를 표시하는 UI 요소를 포함해야 합니다.")]
|
2025-07-01 20:10:15 +09:00
|
|
|
|
[SerializeField]
|
|
|
|
|
|
protected GameObject alarmUIPrefab; // 알람 UI 프리팹 (아래에서 설명)
|
2025-06-23 20:06:15 +09:00
|
|
|
|
private Dictionary<string, AlarmUIController> activeAlarmUIs = new Dictionary<string, AlarmUIController>();
|
|
|
|
|
|
|
2025-07-01 20:10:15 +09:00
|
|
|
|
private List<string> agvNames = new List<string>();
|
|
|
|
|
|
private Dictionary<string, string> alarmAgvNames = new Dictionary<string, string>();
|
|
|
|
|
|
|
2025-06-23 20:06:15 +09:00
|
|
|
|
// FactoryDataManager에서 찾을 수 있도록 참조를 저장
|
2025-06-27 17:50:23 +09:00
|
|
|
|
private FactoryObjectManager? dataManager;
|
2025-06-23 20:06:15 +09:00
|
|
|
|
|
2025-07-01 20:10:15 +09:00
|
|
|
|
|
2025-06-27 17:50:23 +09:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// AlarmManager의 초기화 메서드입니다.
|
|
|
|
|
|
/// Awake 메서드에서 호출되며, MonoBehaviour가 생성될 때 한 번만 실행됩니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
protected override void Init()
|
2025-06-23 20:06:15 +09:00
|
|
|
|
{
|
2025-06-27 17:50:23 +09:00
|
|
|
|
SceneMain.Instance.Initialized += OnSceneInitialized;
|
2025-06-23 20:06:15 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-27 17:50:23 +09:00
|
|
|
|
private void OnSceneInitialized()
|
2025-06-23 20:06:15 +09:00
|
|
|
|
{
|
|
|
|
|
|
dataManager = FactoryObjectManager.Instance;
|
2025-07-01 20:10:15 +09:00
|
|
|
|
|
|
|
|
|
|
//test code
|
|
|
|
|
|
//알람 데이터가 AGV와 관련 없는것이 많아서, AGV 이름을 미리 정의합니다.
|
|
|
|
|
|
for (int i = 1; i <= 115; i++)
|
|
|
|
|
|
{
|
2025-07-02 18:24:00 +09:00
|
|
|
|
//agvNames.Add($"HFF09CNA8{i.ToString("D3")}");
|
|
|
|
|
|
agvNames.Add($"HFF09CNA8061");
|
2025-07-01 20:10:15 +09:00
|
|
|
|
}
|
2025-06-23 20:06:15 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-01 20:10:15 +09:00
|
|
|
|
|
|
|
|
|
|
|
2025-06-27 17:50:23 +09:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Alarm 데이터를 수신하기 위한 MQTT 파이프라인을 설정합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void Run()
|
2025-06-23 20:06:15 +09:00
|
|
|
|
{
|
2025-06-27 17:50:23 +09:00
|
|
|
|
//데이터를 어떤 형식으로 받을지 정의합니다.
|
|
|
|
|
|
var dataMask = new DataMask();
|
|
|
|
|
|
dataMask.ObjectName = "Alarm"; // Alarm 객체의 이름을 설정합니다.
|
|
|
|
|
|
dataMask.ObjectIdKey = "ID"; // Alarm의 고유 식별자로 사용할 키를 설정합니다.
|
|
|
|
|
|
dataMask["ID"] = "";
|
|
|
|
|
|
dataMask["ALARM_TYPE"] = "";
|
|
|
|
|
|
dataMask["LEVEL"] = "";
|
|
|
|
|
|
dataMask["LOGISTIC"] = "";
|
|
|
|
|
|
dataMask["STATE"] = "";
|
|
|
|
|
|
dataMask["MESSAGE"] = "";
|
|
|
|
|
|
dataMask["CODE"] = "";
|
|
|
|
|
|
dataMask["ICON"] = "";
|
|
|
|
|
|
dataMask["MACHINENAME"] = "";
|
|
|
|
|
|
dataMask["SHOPNAME"] = "";
|
|
|
|
|
|
dataMask["TRANSPORT_EQP_NAME"] = "";
|
|
|
|
|
|
dataMask["TRANSPORT_UNIT_NAME"] = "";
|
|
|
|
|
|
dataMask["TRANSPORT_EQP_ID"] = "";
|
|
|
|
|
|
dataMask["TRANSPORT_UNIT_ID"] = "";
|
2025-07-01 20:10:15 +09:00
|
|
|
|
dataMask["CLEAR_TIME"] = DateTime.Now;
|
2025-06-27 17:50:23 +09:00
|
|
|
|
dataMask["SET_TIME"] = DateTime.Now;
|
|
|
|
|
|
dataMask["UPDATE_TIME"] = DateTime.Now;
|
|
|
|
|
|
dataMask["TIMESTAMP"] = DateTime.Now;
|
|
|
|
|
|
|
|
|
|
|
|
// MQTT 파이프라인 정보를 생성합니다.
|
|
|
|
|
|
// 'ALARM' 토픽을 구독하고, 받은 데이터는 위에서 정의한 dataMask로 매핑하며,
|
|
|
|
|
|
// 데이터 유효성 검사를 위해 DataValidator를 설정합니다.
|
|
|
|
|
|
// 데이터가 업데이트되면 OnUpdateData 메서드를 호출하여 처리합니다.
|
|
|
|
|
|
|
|
|
|
|
|
DataValidator validator = new DataValidator();
|
|
|
|
|
|
validator.AddValidator("MACHINENAME", value => value != null);
|
|
|
|
|
|
|
|
|
|
|
|
var pipelineInfo = new MQTTPipeLineInfo("ALARM")
|
|
|
|
|
|
.setDataMapper(new DataMapper(dataMask))
|
|
|
|
|
|
.setValidator(validator)
|
|
|
|
|
|
.setHandler(OnUpdateData);
|
|
|
|
|
|
|
|
|
|
|
|
// 생성한 파이프라인 정보를 전역 MQTT 파이프라인에 추가합니다.
|
|
|
|
|
|
AppMain.Instance.MQTTPipeLine.Add(pipelineInfo);
|
|
|
|
|
|
}
|
2025-07-01 20:10:15 +09:00
|
|
|
|
|
|
|
|
|
|
int agvIdx = 50;
|
|
|
|
|
|
|
2025-06-27 17:50:23 +09:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 데이터 수신 시 호출되는 공개 핸들러입니다.
|
|
|
|
|
|
/// 수신된 ALARM 데이터 배열을 비동기적으로 처리하여 씬에 반영합니다.
|
|
|
|
|
|
/// 추가, 제거, 수정된 ALARM 데이터를 각각 구분하여 처리합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="data">수신된 데이터 객체 (DataArray 형태)</param>
|
|
|
|
|
|
public void OnUpdateData(IDataObject? data)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (data == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
DataArray? arr = data as DataArray;
|
2025-07-01 20:10:15 +09:00
|
|
|
|
if (arr == null || arr.Count == 0) return;
|
2025-06-27 17:50:23 +09:00
|
|
|
|
|
|
|
|
|
|
// 데이터 배열에서 추가, 제거, 수정된 항목 리스트를 가져옵니다.
|
|
|
|
|
|
var AddedItems = arr.AddedItems;
|
2025-07-01 20:10:15 +09:00
|
|
|
|
var RemovedItems = new List<DataObject>(arr.RemovedItems);
|
2025-06-27 17:50:23 +09:00
|
|
|
|
var ModifiedList = arr.ModifiedList;
|
|
|
|
|
|
|
|
|
|
|
|
Debug.Log($"AlarmManager OnUpdateData: Added={AddedItems.Count}, Removed={RemovedItems.Count}, Modified={ModifiedList.Count}");
|
|
|
|
|
|
|
2025-07-01 20:10:15 +09:00
|
|
|
|
// clear_time이 있는 항목만 제거 리스트에 추가합니다.
|
2025-06-27 17:50:23 +09:00
|
|
|
|
foreach (var item in AddedItems.ToList())
|
2025-06-23 20:06:15 +09:00
|
|
|
|
{
|
2025-07-01 20:10:15 +09:00
|
|
|
|
if (item.GetDateTime("CLEAR_TIME") != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (RemovedItems.FindIndex((i) => i.Id == item.Id) == -1) RemovedItems.Add(item);
|
|
|
|
|
|
}
|
2025-06-27 17:50:23 +09:00
|
|
|
|
}
|
2025-07-01 20:10:15 +09:00
|
|
|
|
|
|
|
|
|
|
foreach (var item in ModifiedList.ToList())
|
2025-06-27 17:50:23 +09:00
|
|
|
|
{
|
2025-07-01 20:10:15 +09:00
|
|
|
|
if (item.GetDateTime("CLEAR_TIME") != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (RemovedItems.FindIndex((i) => i.Id == item.Id) == -1) RemovedItems.Add(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 새로 추가된 ALARM 처리
|
|
|
|
|
|
foreach (var item in AddedItems.ToList())
|
|
|
|
|
|
{
|
|
|
|
|
|
if (item.GetDateTime("CLEAR_TIME") == null && !item.Id.IsNullOrEmpty())
|
|
|
|
|
|
{
|
|
|
|
|
|
item["TRANSPORT_EQP_ID"] = agvNames[agvIdx]; // AGV 이름을 TRANSPORT_EQP_ID에 설정
|
|
|
|
|
|
alarmAgvNames.Add(item.Id!, agvNames[agvIdx]);
|
|
|
|
|
|
HandleNewAlarm(item);
|
|
|
|
|
|
agvIdx++;
|
|
|
|
|
|
if(agvIdx >= agvNames.Count) agvIdx = 0; // AGV 이름이 부족할 경우 순환
|
|
|
|
|
|
}
|
2025-06-27 17:50:23 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 정보가 수정된 ALARM 처리
|
|
|
|
|
|
foreach (var item in ModifiedList.ToList())
|
|
|
|
|
|
{
|
2025-07-01 20:10:15 +09:00
|
|
|
|
if (item.GetDateTime("CLEAR_TIME") == null && !item.Id.IsNullOrEmpty() && alarmAgvNames.ContainsKey(item.Id!))
|
|
|
|
|
|
{
|
|
|
|
|
|
item["TRANSPORT_EQP_ID"] = alarmAgvNames[item.Id!]; // 기존 AGV 이름 유지
|
|
|
|
|
|
HandleModifyAlarm(item);
|
|
|
|
|
|
}
|
2025-06-27 17:50:23 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-01 20:10:15 +09:00
|
|
|
|
// 제거된 ALARM 처리
|
|
|
|
|
|
foreach (var item in RemovedItems.ToList())
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!item.Id.IsNullOrEmpty() && alarmAgvNames.ContainsKey(item.Id!))
|
|
|
|
|
|
{
|
|
|
|
|
|
item["TRANSPORT_EQP_ID"] = alarmAgvNames[item.Id!]; // 기존 AGV 이름 유지
|
|
|
|
|
|
HandleClearedAlarm(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-27 17:50:23 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// AlarmManager가 파괴될 때 호출됩니다.
|
|
|
|
|
|
/// MQTT 파이프라인에서 'ALARM' 핸들러를 제거하여 메모리 누수를 방지합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
protected override void OnDestroy()
|
|
|
|
|
|
{
|
|
|
|
|
|
base.OnDestroy();
|
|
|
|
|
|
AppMain.Instance.MQTTPipeLine.Remove("ALARM");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void HandleNewAlarm(DataObject data)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (data.Id == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"New Alarm Received No data. {data}");
|
|
|
|
|
|
return;
|
2025-06-23 20:06:15 +09:00
|
|
|
|
}
|
2025-06-27 17:50:23 +09:00
|
|
|
|
|
2025-07-02 18:24:00 +09:00
|
|
|
|
|
|
|
|
|
|
string? eqpId = data.GetString("TRANSPORT_EQP_ID");
|
|
|
|
|
|
if (eqpId == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
FactoryObject? targetObject = dataManager!.FindByName(eqpId);
|
|
|
|
|
|
Debug.Log($"AlarmManager {targetObject==null} {data.Id}, {eqpId}");
|
|
|
|
|
|
if (targetObject == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
if (activeAlarmUIs.ContainsKey(eqpId))
|
|
|
|
|
|
{
|
|
|
|
|
|
if(activeAlarmUIs[eqpId].ContainsAlarm(data))
|
|
|
|
|
|
{
|
|
|
|
|
|
activeAlarmUIs[eqpId].UpdateAlarm(data);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
activeAlarmUIs[eqpId].AddAlarm(data);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2025-06-27 17:50:23 +09:00
|
|
|
|
{
|
2025-07-02 18:24:00 +09:00
|
|
|
|
// 없으면 새로 생성
|
2025-06-27 17:50:23 +09:00
|
|
|
|
GameObject newUIObject = Instantiate(alarmUIPrefab, transform); // 매니저 하위에 생성
|
|
|
|
|
|
AlarmUIController newUiController = newUIObject.GetComponent<AlarmUIController>();
|
|
|
|
|
|
newUiController.Initialize(targetObject, data);
|
|
|
|
|
|
|
2025-07-02 18:24:00 +09:00
|
|
|
|
activeAlarmUIs.Add(eqpId, newUiController);
|
2025-06-27 17:50:23 +09:00
|
|
|
|
}
|
2025-07-02 18:24:00 +09:00
|
|
|
|
|
2025-06-27 17:50:23 +09:00
|
|
|
|
|
2025-06-23 20:06:15 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-27 17:50:23 +09:00
|
|
|
|
public void HandleModifyAlarm(DataObject data)
|
2025-06-23 20:06:15 +09:00
|
|
|
|
{
|
2025-06-27 17:50:23 +09:00
|
|
|
|
if (data.Id == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"Modify Alarm Received No data. {data}");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-07-02 18:24:00 +09:00
|
|
|
|
string? eqpId = data.GetString("TRANSPORT_EQP_ID");
|
|
|
|
|
|
if (eqpId == null) return;
|
|
|
|
|
|
|
2025-06-27 17:50:23 +09:00
|
|
|
|
// 이미 해당 설비에 알람 UI가 떠 있는지 확인
|
2025-07-02 18:24:00 +09:00
|
|
|
|
if (activeAlarmUIs.TryGetValue(eqpId, out AlarmUIController uiController))
|
2025-06-27 17:50:23 +09:00
|
|
|
|
{
|
2025-07-02 18:24:00 +09:00
|
|
|
|
// 있으면 기존 UI에 알람 정보 업데이트
|
|
|
|
|
|
if (uiController.ContainsAlarm(data))
|
|
|
|
|
|
{
|
|
|
|
|
|
uiController.UpdateAlarm(data);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 없으면 새로 추가
|
|
|
|
|
|
uiController.AddAlarm(data);
|
|
|
|
|
|
}
|
2025-06-27 17:50:23 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void HandleClearedAlarm(DataObject data)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (data.Id.IsNullOrEmpty())
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"Clear Alarm Received No data. {data}");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-07-02 18:24:00 +09:00
|
|
|
|
|
|
|
|
|
|
string? eqpId = data.GetString("TRANSPORT_EQP_ID");
|
|
|
|
|
|
if (eqpId == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
if (activeAlarmUIs.TryGetValue(eqpId, out AlarmUIController uiController))
|
2025-06-23 20:06:15 +09:00
|
|
|
|
{
|
2025-07-02 18:24:00 +09:00
|
|
|
|
// 있으면 기존 UI에 알람 정보 업데이트
|
|
|
|
|
|
if (uiController.ContainsAlarm(data))
|
2025-06-23 20:06:15 +09:00
|
|
|
|
{
|
2025-07-02 18:24:00 +09:00
|
|
|
|
uiController.RemoveAlarm(data);
|
|
|
|
|
|
|
|
|
|
|
|
if (uiController.GetAlarmCount() == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
activeAlarmUIs.Remove(data.Id!);
|
|
|
|
|
|
Destroy(uiController.gameObject);
|
|
|
|
|
|
}
|
2025-06-23 20:06:15 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|