using DevExpress.XtraLayout; using LFP_Manager.DataStructure; using LFP_Manager.Utils; using System; using System.Globalization; using System.Windows.Forms; namespace LFP_Manager.Controls { public partial class ucCalibration : DevExpress.XtraEditors.XtraUserControl { #region DELEGATE public delegate void setCalibUpdate(object sender); public delegate void setCalibCommand(int mode, int index, int flag, ref DeviceParamData aParm, ref DeviceCalibration aCalib); public delegate Int32 getBattData(int item, int cno); #endregion #region ENUMS private enum ControlLevel { User = 0, Technician = 1, Engineer = 2, Master = 3 } private enum CommandMode { SetCapacity = 15, SetChargeMode = 16, SetDateTime = 17, SetAntiTheft = 18, ClearAntiTheft = 19, SetRS485Timeout = 20, SetBalanceVoltage = 21, SetBalanceDiff = 22 } #endregion #region VARIABLES const int CALC_INDEX = 6; CommConfig Config; DeviceSystemData SystemData; DeviceCalibration rCalib; DeviceCalibration wCalib; DeviceParamData rParam; public event setCalibCommand OnCommand = null; int wFlag = 0; int dFlag; int PasswordResult; #endregion #region CONSTRUCTORS // Battery No: // 0: No 1,3,5,7,9,11,13,15 ODD // 1: No 2,4,6,8,10,12,14 Even public ucCalibration() { InitializeComponent(); ComponentLoad(); Config = new CommConfig(); dFlag = 0; } private void ComponentLoad() { CbNewChaMode.Clear(); for (int i = 0; i < csConstData.SystemInfo.CHG_MODE.Length; i++) { CbNewChaMode.Properties.Items.Add(csConstData.SystemInfo.CHG_MODE[i]); } teNewCapacity.KeyPress += TextBox_KeyPress; teNewSoc.KeyPress += TextBox_KeyPress; TeCommTimeoutNew.KeyPress += TextBox_KeyPress; TeChgModeValueNew.KeyPress += TextBox_KeyPress2; } #endregion #region EXT EVENT FUNCTION private void OnCommnadEvent(int mode, int index, int flag, ref DeviceParamData aParam, ref DeviceCalibration aCalib) { OnCommand?.Invoke(mode, index, flag, ref aParam, ref aCalib); } #endregion #region PUBLIC FUCTIONS public void UpdateConfig(CommConfig aConfig) { Config = aConfig; } public void UpdateData(DeviceSystemData aSystemData, DeviceCalibration aCalib) { SystemData = aSystemData; rCalib = aCalib; DisplayCalib(); UpdateNewCalib(); } private void UpdateNewCalib() { if (SystemData != null) { if (wFlag == 0) { teNewCapacity.Text = teCurrCapacity.Text; teNewSoc.Text = teCurrSoc.Text; wFlag = 1; } } } #endregion #region DISPLAY DATA private void DisplayCalib() { if (SystemData != null) { teCurrCapacity.Text = string.Format("{0}", SystemData.ValueData.designedCapacity / 10); teCurrSoc.Text = string.Format("{0}", SystemData.ValueData.rSOC / 10); int mode = SystemData.CalibrationData.ChaMode.Mode; int idx = MapChaModeToComboIndex(mode); if (idx >= 0 && idx < csConstData.SystemInfo.CHG_MODE.Length) TeChaMode.Text = csConstData.SystemInfo.CHG_MODE[idx]; else TeChaMode.Text = string.Format(CultureInfo.InvariantCulture, "UNKNOWN({0})", mode); TeChgModeValueCurr.Text = string.Format("{0:0.00}", Convert.ToDouble(SystemData.CalibrationData.ChaMode.Value) / 100); TeCurrDateTime.Text = SystemData.BmsDateTime.DateTimeStr; // Anti-Theft Data Display if (SystemData.CalibrationData.AntiTheft.GyroScope == 0) { TeAntiTheftGyroCurr.Text = "DISABLE"; } else { TeAntiTheftGyroCurr.Text = "ENABLE"; } TeSensitive.Text = string.Format("{0}", SystemData.CalibrationData.AntiTheft.GyroScope); // Anti-Theft - RS-485 Comm. Timeout TeCommTimeout.Text = string.Format("{0}", SystemData.CalibrationData.AntiTheft.Comm); if (dFlag == 0) { CbNewChaMode.SelectedIndex = 1; CbNewChaMode.SelectedIndex = SystemData.CalibrationData.ChaMode.Mode; TeChgModeValueNew.Text = TeChgModeValueCurr.Text; dFlag = 1; } } } #endregion #region BUTTON EVENT private void TextBox_KeyPress(object sender, KeyPressEventArgs e) { csUtils.TypingOnlyNumber(sender, e, false, false); } private void TextBox_KeyPress2(object sender, KeyPressEventArgs e) { csUtils.TypingOnlyNumber(sender, e, true, false); } private void btnCapacitySet_Click(object sender, EventArgs e) { try { if (!ValidateNumericInput(teNewCapacity.Text, "Capacity") || !ValidateNumericInput(teNewSoc.Text, "SOC")) { return; } wCalib = rCalib.DeepCopy(); wCalib.CapCalib.DesignCapacity = Convert.ToInt32(teNewCapacity.Text) * 10; wCalib.CapCalib.SocValue = Convert.ToInt32(teNewSoc.Text); OnCommnadEvent((int)CommandMode.SetCapacity, CALC_INDEX, 1, ref rParam, ref wCalib); } catch (Exception ex) { ShowError($"Failed to set capacity: {ex.Message}"); } } private void BtnChaModeSet_Click(object sender, EventArgs e) { try { if (string.IsNullOrEmpty(CbNewChaMode.Text)) { ShowError("Please select a charge mode"); return; } if (CbNewChaMode.SelectedIndex > 1 && !ValidateNumericInput(TeChgModeValueNew.Text, "Charge Mode Value")) { return; } wCalib = rCalib.DeepCopy(); wCalib.ChaMode.Mode = CbNewChaMode.SelectedIndex; wCalib.ChaMode.Value = (int)(Convert.ToDouble(TeChgModeValueNew.Text) * 100); OnCommnadEvent((int)CommandMode.SetChargeMode, CALC_INDEX, 1, ref rParam, ref wCalib); } catch (Exception ex) { ShowError($"Failed to set charge mode: {ex.Message}"); } } private void BtnBmsDateTimeSet_Click(object sender, EventArgs e) { try { if (string.IsNullOrEmpty(TeNewDateTime.Text)) { ShowError("Please enter a date/time value"); return; } DateTime dateTime = Convert.ToDateTime(TeNewDateTime.Text); int iDateTime = CalculateDateTimeValue(dateTime); wCalib = rCalib.DeepCopy(); wCalib.BmsDateTime.sValue[0] = (short)(iDateTime >> 16); wCalib.BmsDateTime.sValue[1] = (short)(iDateTime >> 0); OnCommnadEvent((int)CommandMode.SetDateTime, CALC_INDEX, 1, ref rParam, ref wCalib); } catch (Exception ex) { ShowError($"Failed to set date/time: {ex.Message}"); } } private int CalculateDateTimeValue(DateTime dateTime) { return ((dateTime.Year - 2000) << 26) | (dateTime.Month << 22) | (dateTime.Day << 17) | (dateTime.Hour << 12) | (dateTime.Minute << 6) | (dateTime.Second << 0); } private void BtnGetCurrTime_Click(object sender, EventArgs e) { TeNewDateTime.Text = string.Format("{0:yyyy-MM-dd HH:mm:ss}", DateTime.Now); } private void BtnClearAntiTheftProtect_Click(object sender, EventArgs e) { wCalib = rCalib.DeepCopy(); wCalib.AntiTheft.GyroScope = Config.GyroSensitive; OnCommnadEvent(19, CALC_INDEX, 1, ref rParam, ref wCalib); } private void PassWordResultProc(int result) { PasswordResult = result; } private void BtnAntiTheftEnable_Click(object sender, EventArgs e) { wCalib = rCalib.DeepCopy(); wCalib.AntiTheft.GyroScope = Config.GyroSensitive; OnCommnadEvent(18, CALC_INDEX, 1, ref rParam, ref wCalib); } private void BtnAntiTheftDisable_Click(object sender, EventArgs e) { wCalib = rCalib.DeepCopy(); wCalib.AntiTheft.GyroScope = 0; OnCommnadEvent(18, CALC_INDEX, 1, ref rParam, ref wCalib); } private void BtnRS485TimeoutSet_Click(object sender, EventArgs e) { string stTimeout = TeCommTimeoutNew.Text; int Timeout = 0; try { Timeout = Convert.ToInt32(stTimeout); } catch (Exception) { MessageBox.Show("Timeout data Format Error", "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } wCalib = rCalib.DeepCopy(); wCalib.AntiTheft.Comm = Timeout; OnCommnadEvent(20, CALC_INDEX, 1, ref rParam, ref wCalib); } #endregion #region COMPONENT EVENT private void CbNewChaMode_SelectedIndexChanged(object sender, EventArgs e) { switch (CbNewChaMode.SelectedIndex) { case 0: TeChgModeValueNew.Enabled = false; break; case 1: TeChgModeValueNew.Enabled = false; break; case 2: TeChgModeValueNew.Enabled = true; break; case 3: TeChgModeValueNew.Enabled = true; break; case 4: TeChgModeValueNew.Enabled = true; break; default: TeChgModeValueNew.Enabled = false; break; } } #endregion #region FORM EVENT private void ucCalibration_Load(object sender, EventArgs e) { CalibConfig_ReLoad(); } private void CalibConfig_ReLoad() { var level = (ControlLevel)Config.ControlLevel; btnCapacitySet.Enabled = level == ControlLevel.Master; BtnChaModeSet.Enabled = level >= ControlLevel.Engineer; BtnBmsDateTimeSet.Enabled = level >= ControlLevel.Engineer; BtnClearAntiTheftProtect.Enabled = level >= ControlLevel.Technician; BtnAntiTheftEnable.Enabled = level >= ControlLevel.Engineer; BtnAntiTheftDisable.Enabled = level >= ControlLevel.Engineer; BtnRS485TimeoutSet.Enabled = level >= ControlLevel.Technician; } #endregion #region VALIDATION METHODS private bool ValidateNumericInput(string input, string fieldName) { if (string.IsNullOrEmpty(input)) { ShowError($"Please enter a value for {fieldName}"); return false; } if (!int.TryParse(input, out _)) { ShowError($"Invalid numeric value for {fieldName}"); return false; } return true; } private void ShowError(string message) { MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } #endregion #region HELPER FUNCTIONS // Charging Mode 매핑: 콤보 인덱스 <-> ChaMode.Mode private static bool TryMapIndexToChaMode(int comboIndex, out int mode) { // UI: 0=CC,1=CV,2=CC-CV(x),3=Time(x),4=Power(x) (예시) // 장비: 0,1,2,4,8 로 매핑 switch (comboIndex) { case 0: mode = 0; return true; case 1: mode = 1; return true; case 2: mode = 2; return true; case 3: mode = 4; return true; case 4: mode = 8; return true; default: mode = -1; return false; } } private static int MapChaModeToComboIndex(int mode) { switch (mode) { case 0: return 0; case 1: return 1; case 2: return 2; case 4: return 3; case 8: return 4; default: return -1; } } private static bool TryParseDouble(string text, out double value) { return double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out value); } private static string ToInvStr(double v, string fmt) => v.ToString(fmt, CultureInfo.InvariantCulture); // BMS DateTime 비트 인코딩 (검증 포함) private static bool TryEncodeBmsDateTime(DateTime dt, out short hi, out short lo, out string error) { error = null; int year = dt.Year; if (year < 2000 || year > 2063) { error = "Year must be 2000~2063"; hi = lo = 0; return false; } int y = year - 2000; int m = dt.Month; if (m < 1 || m > 12) { error = "Month out of range"; hi = lo = 0; return false; } int d = dt.Day; if (d < 1 || d > 31) { error = "Day out of range"; hi = lo = 0; return false; } int hh = dt.Hour; if (hh < 0 || hh > 23) { error = "Hour out of range"; hi = lo = 0; return false; } int mm = dt.Minute; if (mm < 0 || mm > 59) { error = "Minute out of range"; hi = lo = 0; return false; } int ss = dt.Second; if (ss < 0 || ss > 59) { error = "Second out of range"; hi = lo = 0; return false; } int packed = (y << 26) | (m << 22) | (d << 17) | (hh << 12) | (mm << 6) | ss; hi = (short)(packed >> 16); lo = (short)(packed & 0xFFFF); return true; } #endregion } }