Newer
Older
powermon_manager_sw / gui / gui_device_config.cpp
@Razvan Turiac Razvan Turiac on 8 Jul 60 KB Initial import
/* Copyright (C) 2020 - 2024, Thornwave Labs Inc
 * Written by Razvan Turiac <razvan.turiac@thornwave.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
 * documentation files (the “Software”), to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 * Attribution shall be given to Thornwave Labs Inc. and shall be made visible to the final user. 
 * 
 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
 * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include <gui_device_config.h>


typedef struct 
{
	uint16_t current_nom;
	uint8_t vdrop_nom;
	bool integrated;
}shunt_descriptor_t;


static const shunt_descriptor_t l_shunts[] = 
{
	{.current_nom = 60, .vdrop_nom = 30, .integrated = true},

	{.current_nom = 75, .vdrop_nom = 50, .integrated = false},

	{.current_nom = 100, .vdrop_nom = 50, .integrated = false},
	{.current_nom = 100, .vdrop_nom = 75, .integrated = false},
	{.current_nom = 100, .vdrop_nom = 100, .integrated = false},

	{.current_nom = 150, .vdrop_nom = 50, .integrated = false},
	{.current_nom = 150, .vdrop_nom = 75, .integrated = false},
	{.current_nom = 150, .vdrop_nom = 100, .integrated = false},
	
	{.current_nom = 200, .vdrop_nom = 50, .integrated = false},
	{.current_nom = 200, .vdrop_nom = 75, .integrated = false},
	{.current_nom = 200, .vdrop_nom = 100, .integrated = false},

	{.current_nom = 250, .vdrop_nom = 50, .integrated = false},
	{.current_nom = 250, .vdrop_nom = 75, .integrated = false},
	{.current_nom = 250, .vdrop_nom = 100, .integrated = false},

	{.current_nom = 300, .vdrop_nom = 50, .integrated = false},
	{.current_nom = 300, .vdrop_nom = 75, .integrated = false},
	{.current_nom = 300, .vdrop_nom = 100, .integrated = false},

	{.current_nom = 400, .vdrop_nom = 50, .integrated = false},
	{.current_nom = 400, .vdrop_nom = 75, .integrated = false},
	{.current_nom = 400, .vdrop_nom = 100, .integrated = false},

	{.current_nom = 500, .vdrop_nom = 50, .integrated = false},
	{.current_nom = 500, .vdrop_nom = 75, .integrated = false},
	{.current_nom = 500, .vdrop_nom = 100, .integrated = false},

	{.current_nom = 1000, .vdrop_nom = 50, .integrated = false},
	{.current_nom = 1000, .vdrop_nom = 75, .integrated = false},
	{.current_nom = 1000, .vdrop_nom = 100, .integrated = false},
};


GuiDeviceConfig::GuiDeviceConfig(wxWindow *parent, wxWindowID id, const PowermonConfig &config, const Powermon &powermon): wxDialog(parent, id, wxT("PowerMon Configuration"), wxDefaultPosition, wxDefaultSize), mConfig(config), mPowermon(powermon)
{
	const size_t BORDER_SIZE = FromDIP(6);
	const size_t GAP_SIZE = FromDIP(3);
	const wxSize CTRL_SIZE = FromDIP(wxSize(240, 30));
	const wxSize SIZER_SIZE = FromDIP(wxSize(280, 0));

	wxBoxSizer* v_sizer;
	wxBoxSizer* h_sizer;
	wxBoxSizer* main_v_sizer;

	//main window sizer
	wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
	SetSizer(main_sizer);

	
	mListBook = new wxListbook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLB_LEFT);
	main_sizer->Add(mListBook, 1, wxALL | wxEXPAND, BORDER_SIZE);


	wxPanel* panel = new wxPanel(mListBook, wxID_ANY, wxDefaultPosition, wxDefaultSize);
	mListBook->AddPage(panel, wxT("General"));
	main_v_sizer = new wxBoxSizer(wxVERTICAL);


	if (Powermon::hasWifi(mPowermon.getLastDeviceInfo().hardware_revision_bcd))
	{
		mWifiKeepApOnCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("Keep Access Point ON"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
		main_v_sizer->Add(mWifiKeepApOnCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);
	}
	

	if (Powermon::hasConfigurableShunt(mPowermon.getLastDeviceInfo().hardware_revision_bcd))
	{
		mShuntChoice = new wxChoice(panel, ID_CHOICE_SHUNT, wxDefaultPosition, CTRL_SIZE);
		for(uint32_t i = 0; i < sizeof(l_shunts) / sizeof(shunt_descriptor_t); i++)
			mShuntChoice->Append(wxString::Format(wxT("%u A / %u mV%s"), l_shunts[i].current_nom, l_shunts[i].vdrop_nom, l_shunts[i].integrated ? " (integrated)" : ""));

		v_sizer = new wxBoxSizer(wxVERTICAL);
		v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Shunt Specifications"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
		v_sizer->Add(mShuntChoice, 0, wxALL, 0);
		main_v_sizer->Add(v_sizer, 0, wxALL, BORDER_SIZE);
	}


	mMaxShuntCurrentText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Max. Shunt Current (Amperes)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mMaxShuntCurrentText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL, BORDER_SIZE);


	if (Powermon::hasConfigurableShunt(mPowermon.getLastDeviceInfo().hardware_revision_bcd))
	{
		mAdcFlipCurrentSignCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("Flip Current Sign"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
		main_v_sizer->Add(mAdcFlipCurrentSignCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);
	}


	mAdcDisableV2CheckBox = new wxCheckBox(panel, ID_CHECKBOX_DISABLE_V2, wxT("Disable Voltage 2"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mAdcDisableV2CheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mMeterVoltageSourceCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("Use Voltage 2 for Power"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mMeterVoltageSourceCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcInitialStateCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("Turn On at startup"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcInitialStateCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcLatchRelayOnCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("Latch Relay On"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcLatchRelayOnCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcInvertOutputCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("Invert Relay Logic"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcInvertOutputCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcConnectFilterText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Connect Filter (milliseconds)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcConnectFilterText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mMfTerminalFunctionChoice = new wxChoice(panel, wxID_ANY, wxDefaultPosition, CTRL_SIZE);
	mMfTerminalFunctionChoice->Append(wxT("Data Stream Ouput"));
	mMfTerminalFunctionChoice->Append(wxT("Temperature Sensor"));
	mMfTerminalFunctionChoice->Append(wxT("Push Button Input"));
	mMfTerminalFunctionChoice->Append(wxT("Communication"));
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("MF Terminal Function"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mMfTerminalFunctionChoice, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);


	mLoggingModeChoice = new wxChoice(panel, wxID_ANY, wxDefaultPosition, CTRL_SIZE);
	mLoggingModeChoice->Append(wxT("Disabled"));
	mLoggingModeChoice->Append(wxT("Every second"));
	mLoggingModeChoice->Append(wxT("Every 2 seconds"));
	mLoggingModeChoice->Append(wxT("Every 5 seconds"));
	mLoggingModeChoice->Append(wxT("Every 10 seconds"));
	mLoggingModeChoice->Append(wxT("Every 20 seconds"));
	mLoggingModeChoice->Append(wxT("Every 30 seconds"));
	mLoggingModeChoice->Append(wxT("Every 60 seconds"));
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Data Logging Mode"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mLoggingModeChoice, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	main_v_sizer->SetMinSize(SIZER_SIZE);
	panel->SetSizer(main_v_sizer);



	panel = new wxPanel(mListBook, wxID_ANY, wxDefaultPosition, wxDefaultSize);
	mListBook->AddPage(panel, wxT("Low Voltage Disconnect"));
	main_v_sizer = new wxBoxSizer(wxVERTICAL);

	mOcLvdEnableCheckBox = new wxCheckBox(panel, ID_CHECKBOX_ENABLE_FEATURE, wxT("LVD Enable"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcLvdEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcLvdVoltageSourceCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("Use Voltage 2"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcLvdVoltageSourceCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcLvdDisconnectThresholdText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("LVD Disconnect Threshold (V)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcLvdDisconnectThresholdText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcLvdConnectThresholdText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("LVD Connect Threshold (V)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcLvdConnectThresholdText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcLvdDisconnectFilterText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("LVD Disconnect Filter (milliseconds)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcLvdDisconnectFilterText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	main_v_sizer->SetMinSize(SIZER_SIZE);
	panel->SetSizer(main_v_sizer);



	panel = new wxPanel(mListBook, wxID_ANY, wxDefaultPosition, wxDefaultSize);
	mListBook->AddPage(panel, wxT("High Voltage Disconnect"));
	main_v_sizer = new wxBoxSizer(wxVERTICAL);

	mOcHvdEnableCheckBox = new wxCheckBox(panel, ID_CHECKBOX_ENABLE_FEATURE, wxT("HVD Enable"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcHvdEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcHvdVoltageSourceCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("Use Voltage 2"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcHvdVoltageSourceCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcHvdDisconnectThresholdText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("HVD Disconnect Threshold (V)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcHvdDisconnectThresholdText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcHvdConnectThresholdText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("HVD Connect Threshold (V)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcHvdConnectThresholdText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcHvdDisconnectFilterText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("HVD Disconnect Filter (milliseconds)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcHvdDisconnectFilterText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	main_v_sizer->SetMinSize(SIZER_SIZE);
	panel->SetSizer(main_v_sizer);


	
	panel = new wxPanel(mListBook, wxID_ANY, wxDefaultPosition, wxDefaultSize);
	mListBook->AddPage(panel, wxT("Over-current Disconnect"));
	main_v_sizer = new wxBoxSizer(wxVERTICAL);

	mOcOcdEnableCheckBox = new wxCheckBox(panel, ID_CHECKBOX_ENABLE_FEATURE, wxT("OCD Enable"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcOcdEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcOcdTripThresholdText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("OCD Trip Threshold (A)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcOcdTripThresholdText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcOcdTripFilterText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("OCD Trip Filter (milliseconds)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcOcdTripFilterText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	main_v_sizer->SetMinSize(SIZER_SIZE);
	panel->SetSizer(main_v_sizer);


	panel = new wxPanel(mListBook, wxID_ANY, wxDefaultPosition, wxDefaultSize);
	mListBook->AddPage(panel, wxT("Auto-OFF / Auto-ON Timer"));
	main_v_sizer = new wxBoxSizer(wxVERTICAL);


	mOcAutoOffEnableCheckBox = new wxCheckBox(panel, ID_CHECKBOX_ENABLE_FEATURE, wxT("Auto-OFF Timer Enable"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcAutoOffEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcAutoOffHoursText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Hours"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcAutoOffHoursText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcAutoOffMinutesText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Minutes"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcAutoOffMinutesText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcAutoOffSecondsText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Seconds"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcAutoOffSecondsText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);



	mOcAutoOnEnableCheckBox = new wxCheckBox(panel, ID_CHECKBOX_ENABLE_FEATURE, wxT("Auto-ON Timer Enable"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcAutoOnEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcAutoOnHoursText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Hours"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcAutoOnHoursText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcAutoOnMinutesText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Minutes"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcAutoOnMinutesText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcAutoOnSecondsText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Seconds"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcAutoOnSecondsText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);


	main_v_sizer->SetMinSize(SIZER_SIZE);
	panel->SetSizer(main_v_sizer);



	panel = new wxPanel(mListBook, wxID_ANY, wxDefaultPosition, wxDefaultSize);
	mListBook->AddPage(panel, wxT("Low Temperature Disconnect"));
	main_v_sizer = new wxBoxSizer(wxVERTICAL);

	mOcLtdEnableCheckBox = new wxCheckBox(panel, ID_CHECKBOX_ENABLE_FEATURE, wxT("LTD Enable"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcLtdEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcLtdDisconnectThresholdText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("LTD Disconnect Threshold (V)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcLtdDisconnectThresholdText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcLtdConnectThresholdText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("LTD Connect Threshold (V)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcLtdConnectThresholdText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcLtdDisconnectFilterText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("LTD Disconnect Filter (milliseconds)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcLtdDisconnectFilterText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	main_v_sizer->SetMinSize(SIZER_SIZE);
	panel->SetSizer(main_v_sizer);


	panel = new wxPanel(mListBook, wxID_ANY, wxDefaultPosition, wxDefaultSize);
	mListBook->AddPage(panel, wxT("High Temperature Disconnect"));
	main_v_sizer = new wxBoxSizer(wxVERTICAL);

	mOcHtdEnableCheckBox = new wxCheckBox(panel, ID_CHECKBOX_ENABLE_FEATURE, wxT("HTD Enable"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcHtdEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcHtdDisconnectThresholdText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("HTD Disconnect Threshold (V)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcHtdDisconnectThresholdText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcHtdConnectThresholdText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("HTD Connect Threshold (V)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcHtdConnectThresholdText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcHtdDisconnectFilterText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("HTD Disconnect Filter (milliseconds)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcHtdDisconnectFilterText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	main_v_sizer->SetMinSize(SIZER_SIZE);
	panel->SetSizer(main_v_sizer);


	panel = new wxPanel(mListBook, wxID_ANY, wxDefaultPosition, wxDefaultSize);
	mListBook->AddPage(panel, wxT("Battery Fuel Gauge"));
	main_v_sizer = new wxBoxSizer(wxVERTICAL);

	mFgEnableCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("FG Enable"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mFgEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mFgVoltageSourceCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("Use Voltage 2"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mFgVoltageSourceCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);
	
	mFgChemistryChoice = new wxChoice(panel, ID_CHOICE_FG_CHEMISTRY, wxDefaultPosition, CTRL_SIZE);
	mFgChemistryChoice->Append(wxT("Lead Acid - Flooded"));
	mFgChemistryChoice->Append(wxT("Lead Acid - AGM"));
	mFgChemistryChoice->Append(wxT("Lithium Iron Phosphate"));
	mFgChemistryChoice->Append(wxT("Lithium Ion"));
	mFgChemistryChoice->Append(wxT("Lithium Polymer"));
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Battery Chemistry"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mFgChemistryChoice, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mFgBatteryVoltageChoice = new wxChoice(panel, ID_CHOICE_FG_VOLTAGE, wxDefaultPosition, CTRL_SIZE);
	mFgBatteryVoltageChoice->Append(wxT("6 Volts"));
	mFgBatteryVoltageChoice->Append(wxT("12 Volts"));
	mFgBatteryVoltageChoice->Append(wxT("24 Volts"));
	mFgBatteryVoltageChoice->Append(wxT("36 Volts"));
	mFgBatteryVoltageChoice->Append(wxT("48 Volts"));
	mFgBatteryVoltageChoice->Append(wxT("Custom"));
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Battery Voltage"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mFgBatteryVoltageChoice, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mFgCellCountText = new wxTextCtrl(panel, ID_TEXT_FG_CELLS, wxT(""), wxDefaultPosition, CTRL_SIZE);
	mFgCellCountSizer = new wxBoxSizer(wxVERTICAL);
	mFgCellCountSizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Battery Cell Count"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	mFgCellCountSizer->Add(mFgCellCountText, 0, wxALL, 0);
	main_v_sizer->Add(mFgCellCountSizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mFgCapacityText = new wxTextCtrl(panel, ID_TEXT_FG_CAPACITY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Battery Capacity (Ah)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mFgCapacityText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mFgManualSyncCheckBox = new wxCheckBox(panel, ID_CHECK_MANUAL_DETECTION, wxT("Manual Full Charge Detection"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mFgManualSyncCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);


	mFgSyncManualSizer = new wxBoxSizer(wxVERTICAL);

	mFgSyncVoltageThresholdText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Full Charge Detection Voltage Threshold (V)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mFgSyncVoltageThresholdText, 0, wxALL, 0);
	mFgSyncManualSizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mFgSyncCurrentThresholdText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Full Charge Detection Current Threshold (A)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mFgSyncCurrentThresholdText, 0, wxALL, 0);
	mFgSyncManualSizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mFgSyncFilterText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Full Charge Detection Filter (seconds)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mFgSyncFilterText, 0, wxALL, 0);
	mFgSyncManualSizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);
	
	main_v_sizer->Add(mFgSyncManualSizer, 0, wxALL | wxALIGN_LEFT, 0);



	mOcFgDisconnectEnableCheckBox = new wxCheckBox(panel, ID_CHECKBOX_ENABLE_FEATURE, wxT("Disconnect on Battery SoC"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcFgDisconnectEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);
	
	mOcFgDisconnectThresholdText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Disconnect On Battery SoC Threshold (%)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcFgDisconnectThresholdText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcFgConnectEnableCheckBox = new wxCheckBox(panel, ID_CHECKBOX_ENABLE_FEATURE, wxT("Re-connect on Battery SoC"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcFgConnectEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcFgConnectThresholdText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Re-connect On Battery SoC Threshold (%)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcFgConnectThresholdText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	main_v_sizer->SetMinSize(SIZER_SIZE);
	panel->SetSizer(main_v_sizer);



	panel = new wxPanel(mListBook, wxID_ANY, wxDefaultPosition, wxDefaultSize);
	mListBook->AddPage(panel, wxT("Generator Control"));
	main_v_sizer = new wxBoxSizer(wxVERTICAL);


	mOcGenEnableCheckBox = new wxCheckBox(panel, ID_CHECKBOX_ENABLE_GENERATOR, wxT("Generator Control Enable"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcGenEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcGenVoltageSourceCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("Use Voltage 2"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcGenVoltageSourceCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcGenVonEnableCheckBox = new wxCheckBox(panel, ID_CHECK_VON_GENERATOR, wxT("Turn On Based on Voltage"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcGenVonEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcGenVonSizer = new wxBoxSizer(wxVERTICAL);

	mOcGenVonThresholdText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Turn On Voltage Threshold (V)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcGenVonThresholdText, 0, wxALL, 0);
	mOcGenVonSizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcGenVonFilterText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Turn On Voltage Filter (milliseconds)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcGenVonFilterText, 0, wxALL, 0);
	mOcGenVonSizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);
	main_v_sizer->Add(mOcGenVonSizer, 0, wxALL | wxALIGN_LEFT, 0);


	mOcGenSoconEnableCheckBox = new wxCheckBox(panel, ID_CHECK_VON_GENERATOR, wxT("Turn On Based on SoC"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcGenSoconEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcGenSoconThresholdText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	mOcGenSoconSizer = new wxBoxSizer(wxVERTICAL);
	mOcGenSoconSizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Turn On SoC Threshold (%)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	mOcGenSoconSizer->Add(mOcGenSoconThresholdText, 0, wxALL, 0);
	main_v_sizer->Add(mOcGenSoconSizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcGenVoffEnableCheckBox = new wxCheckBox(panel, ID_CHECK_VON_GENERATOR, wxT("Turn Off Based on Voltage"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcGenVoffEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcGenVoffThresholdText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	mOcGenVoffSizer = new wxBoxSizer(wxVERTICAL);
	mOcGenVoffSizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Turn Off Voltage Threshold (V)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	mOcGenVoffSizer->Add(mOcGenVoffThresholdText, 0, wxALL, 0);
	main_v_sizer->Add(mOcGenVoffSizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcGenSocoffEnableCheckBox = new wxCheckBox(panel, ID_CHECK_VON_GENERATOR, wxT("Turn Off Based on SoC"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
	main_v_sizer->Add(mOcGenSocoffEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcGenSocoffThresholdText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	mOcGenSocoffSizer = new wxBoxSizer(wxVERTICAL);
	mOcGenSocoffSizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Turn Off SoC Threshold (%)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	mOcGenSocoffSizer->Add(mOcGenSocoffThresholdText, 0, wxALL, 0);
	main_v_sizer->Add(mOcGenSocoffSizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	mOcGenTurnOffDelayText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
	v_sizer = new wxBoxSizer(wxVERTICAL);
	v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Turn Off Delay (minutes)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
	v_sizer->Add(mOcGenTurnOffDelayText, 0, wxALL, 0);
	main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

	main_v_sizer->SetMinSize(SIZER_SIZE);
	panel->SetSizer(main_v_sizer);


	if (Powermon::hasVoltage2(mPowermon.getLastDeviceInfo().hardware_revision_bcd))
	{
		panel = new wxPanel(mListBook, wxID_ANY, wxDefaultPosition, wxDefaultSize);
		mListBook->AddPage(panel, wxT("LiFePO Charge Manager"));
		main_v_sizer = new wxBoxSizer(wxVERTICAL);

		mOcLiFePOEnableCheckBox = new wxCheckBox(panel, ID_CHECKBOX_ENABLE_LIFEPO_CM, wxT("LiFePO Charge Manager Enable"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
		main_v_sizer->Add(mOcLiFePOEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

		mOcLiFePOBatteryVoltageChoice = new wxChoice(panel, ID_CHOICE_LIFEPO_CM_VOLTAGE, wxDefaultPosition, CTRL_SIZE);
		mOcLiFePOBatteryVoltageChoice->Append(wxT("6 Volts"));
		mOcLiFePOBatteryVoltageChoice->Append(wxT("12 Volts"));
		mOcLiFePOBatteryVoltageChoice->Append(wxT("24 Volts"));
		mOcLiFePOBatteryVoltageChoice->Append(wxT("36 Volts"));
		mOcLiFePOBatteryVoltageChoice->Append(wxT("48 Volts"));
		mOcLiFePOBatteryVoltageChoice->Append(wxT("Custom"));
		v_sizer = new wxBoxSizer(wxVERTICAL);
		v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Battery Voltage"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
		v_sizer->Add(mOcLiFePOBatteryVoltageChoice, 0, wxALL, 0);
		main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

		mOcLiFePOCellCountText = new wxTextCtrl(panel, ID_CHOICE_LIFEPO_CM_CELLS, wxT(""), wxDefaultPosition, CTRL_SIZE);
		mOcLiFePOCellCountSizer = new wxBoxSizer(wxVERTICAL);
		mOcLiFePOCellCountSizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Battery Cell Count"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
		mOcLiFePOCellCountSizer->Add(mOcLiFePOCellCountText, 0, wxALL, 0);
		main_v_sizer->Add(mOcLiFePOCellCountSizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

		mOcLiFePOBatteryCapacityText = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxDefaultPosition, CTRL_SIZE);
		v_sizer = new wxBoxSizer(wxVERTICAL);
		v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Battery Capacity (Ah)"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE);
		v_sizer->Add(mOcLiFePOBatteryCapacityText, 0, wxALL, 0);
		main_v_sizer->Add(v_sizer, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE);

		main_v_sizer->SetMinSize(SIZER_SIZE);
		panel->SetSizer(main_v_sizer);
	}

	
	//add buttons
	h_sizer = new wxBoxSizer(wxHORIZONTAL);

	mCancelButton = new wxButton(this, wxID_CANCEL, wxT("Cancel"), wxDefaultPosition, FromDIP(wxSize(160, 40)));

	h_sizer->AddStretchSpacer(1);
	h_sizer->Add(mCancelButton, 0, wxALL | wxEXPAND, BORDER_SIZE);
	h_sizer->AddStretchSpacer(1);
	
	mSaveButton = new wxButton(this, wxID_OK, wxT("Save Configuration"), wxDefaultPosition, FromDIP(wxSize(160, 40)));
	h_sizer->Add(mSaveButton, 0, wxALL | wxEXPAND, BORDER_SIZE);
	h_sizer->AddStretchSpacer(1);

	main_sizer->Add(h_sizer, 0, wxALL | wxEXPAND, BORDER_SIZE);
	
	main_sizer->SetSizeHints(this);
	
	
	if (Powermon::hasWifi(mPowermon.getLastDeviceInfo().hardware_revision_bcd))
		mWifiKeepApOnCheckBox->SetValue(mConfig.getWiFiKeepAPOn());

	if (Powermon::hasConfigurableShunt(mPowermon.getLastDeviceInfo().hardware_revision_bcd))
	{
		mShuntChoice->SetSelection(-1);

		const uint16_t current_nom = mConfig.getEngineShuntCurrentNom();
		const uint8_t vdrop_nom = mConfig.getEngineShuntVdropNom();

		for(uint32_t i = 0; i < sizeof(l_shunts) / sizeof(shunt_descriptor_t); i++)
		{
			if ((l_shunts[i].current_nom == current_nom) && (l_shunts[i].vdrop_nom == vdrop_nom))
			{
				mShuntChoice->SetSelection(i);
				break;
			}
		}

		mAdcFlipCurrentSignCheckBox->SetValue(mConfig.getEngineCurrentSignFlip());
	}

	mMaxShuntCurrentText->SetValue(wxString::Format(wxT("%u"), mConfig.getEngineShuntCurrentMax()));
	
	
	if (Powermon::hasVoltage2(mPowermon.getLastDeviceInfo().hardware_revision_bcd))
	{
		mAdcDisableV2CheckBox->SetValue(mConfig.getEngineDisableV2());

		mMeterVoltageSourceCheckBox->SetValue(mConfig.getEngineMeterVoltageSource());
		mOcLvdVoltageSourceCheckBox->SetValue(mConfig.getOcLvdSource());
		mOcHvdVoltageSourceCheckBox->SetValue(mConfig.getOcHvdSource());
		mFgVoltageSourceCheckBox->SetValue(mConfig.getFgVoltageSource());
		mOcGenVoltageSourceCheckBox->SetValue(mConfig.getOcGenVonEnable() && mConfig.getOcGenVoltageSource());

		mOcLiFePOEnableCheckBox->SetValue(mConfig.getOcLiFePOEnable());
		mOcLiFePOCellCountText->SetValue(wxString::Format(wxT("%u"), mConfig.getOcLiFePOCellCount()));
		switch(mConfig.getOcLiFePOCellCount())
		{
			case 2: mOcLiFePOBatteryVoltageChoice->SetSelection(0); break;
			case 4: mOcLiFePOBatteryVoltageChoice->SetSelection(1); break;
			case 8: mOcLiFePOBatteryVoltageChoice->SetSelection(2); break;
			case 12: mOcLiFePOBatteryVoltageChoice->SetSelection(3); break;
			case 16: mOcLiFePOBatteryVoltageChoice->SetSelection(4); break;
			default: mOcLiFePOBatteryVoltageChoice->SetSelection(5); break;
		}

		mOcLiFePOCellCountSizer->ShowItems(mOcLiFePOBatteryVoltageChoice->GetSelection() == 5);
		mOcLiFePOBatteryCapacityText->SetValue(wxString::Format(wxT("%.1f"), mConfig.getOcLiFePODesignCapacity()));
	
		if (mAdcDisableV2CheckBox->GetValue())
		{
			mMeterVoltageSourceCheckBox->Hide();
			mOcLvdVoltageSourceCheckBox->Hide();
			mOcHvdVoltageSourceCheckBox->Hide();
			mFgVoltageSourceCheckBox->Hide();
			mOcGenVoltageSourceCheckBox->Hide();
		}
	}
	else
	{
		mAdcDisableV2CheckBox->SetValue(true);
		mAdcDisableV2CheckBox->Hide();

		mMeterVoltageSourceCheckBox->Hide();
		mOcLvdVoltageSourceCheckBox->Hide();
		mOcHvdVoltageSourceCheckBox->Hide();
		mFgVoltageSourceCheckBox->Hide();
		mOcGenVoltageSourceCheckBox->Hide();
	}


	mOcInitialStateCheckBox->SetValue(mConfig.getOcInitialState());
	mOcLatchRelayOnCheckBox->SetValue(mConfig.getOcLatchRelayOn());
	mOcInvertOutputCheckBox->SetValue(mConfig.getOcInvertOutput());

	mOcConnectFilterText->SetValue(wxString::Format(wxT("%u"), mConfig.getOcConnectFilter()));

	mMfTerminalFunctionChoice->SetSelection(mConfig.getMfMode());
	mLoggingModeChoice->SetSelection(mConfig.getLogMode());


	mOcLvdEnableCheckBox->SetValue(mConfig.getOcLvdEnable());
	mOcLvdDisconnectThresholdText->SetValue(wxString::Format(wxT("%.3f"), mConfig.getOcLvdDisconnectThreshold()));
	mOcLvdConnectThresholdText->SetValue(wxString::Format(wxT("%.3f"), mConfig.getOcLvdConnectThreshold()));
	mOcLvdDisconnectFilterText->SetValue(wxString::Format(wxT("%u"), mConfig.getOcLvdDisconnectFilter()));

	mOcOcdEnableCheckBox->SetValue(mConfig.getOcOcdEnable());
	mOcOcdTripThresholdText->SetValue(wxString::Format(wxT("%.3f"), mConfig.getOcOcdTripThreshold()));
	mOcOcdTripFilterText->SetValue(wxString::Format(wxT("%u"), mConfig.getOcOcdTripFilter()));

	mOcHvdEnableCheckBox->SetValue(mConfig.getOcHvdEnable());
	mOcHvdDisconnectThresholdText->SetValue(wxString::Format(wxT("%.3f"), mConfig.getOcHvdDisconnectThreshold()));
	mOcHvdConnectThresholdText->SetValue(wxString::Format(wxT("%.3f"), mConfig.getOcHvdConnectThreshold()));
	mOcHvdDisconnectFilterText->SetValue(wxString::Format(wxT("%u"), mConfig.getOcHvdDisconnectFilter()));


	mOcAutoOffEnableCheckBox->SetValue(mConfig.getOcAutoOffTimer() != 0);
	if (mConfig.getOcAutoOffTimer())
	{
		uint32_t seconds = mConfig.getOcAutoOffTimer();
		uint32_t hours = seconds / 3600;
		seconds -= hours * 3600;
		uint32_t minutes = seconds / 60;
		seconds -= minutes * 60;

		mOcAutoOffHoursText->SetValue(wxString::Format(wxT("%u"), hours));
		mOcAutoOffMinutesText->SetValue(wxString::Format(wxT("%u"), minutes));
		mOcAutoOffSecondsText->SetValue(wxString::Format(wxT("%u"), seconds));
	}
	else
	{
		mOcAutoOffHoursText->SetValue(wxT("0"));
		mOcAutoOffMinutesText->SetValue(wxT("0"));
		mOcAutoOffSecondsText->SetValue(wxT("0"));
	}

	mOcAutoOnEnableCheckBox->SetValue(mConfig.getOcAutoOnTimer() != 0);
	if (mConfig.getOcAutoOnTimer())
	{
		uint32_t seconds = mConfig.getOcAutoOnTimer();
		uint32_t hours = seconds / 3600;
		seconds -= hours * 3600;
		uint32_t minutes = seconds / 60;
		seconds -= minutes * 60;

		mOcAutoOnHoursText->SetValue(wxString::Format(wxT("%u"), hours));
		mOcAutoOnMinutesText->SetValue(wxString::Format(wxT("%u"), minutes));
		mOcAutoOnSecondsText->SetValue(wxString::Format(wxT("%u"), seconds));
	}
	else
	{
		mOcAutoOnHoursText->SetValue(wxT("0"));
		mOcAutoOnMinutesText->SetValue(wxT("0"));
		mOcAutoOnSecondsText->SetValue(wxT("0"));
	}

	mOcLtdEnableCheckBox->SetValue(mConfig.getOcLtdEnable());
	mOcLtdDisconnectThresholdText->SetValue(wxString::Format(wxT("%d"), mConfig.getOcLtdDisconnectThreshold()));
	mOcLtdConnectThresholdText->SetValue(wxString::Format(wxT("%d"), mConfig.getOcLtdConnectThreshold()));
	mOcLtdDisconnectFilterText->SetValue(wxString::Format(wxT("%u"), mConfig.getOcLtdDisconnectFilter()));

	mOcHtdEnableCheckBox->SetValue(mConfig.getOcHtdEnable());
	mOcHtdDisconnectThresholdText->SetValue(wxString::Format(wxT("%d"), mConfig.getOcHtdDisconnectThreshold()));
	mOcHtdConnectThresholdText->SetValue(wxString::Format(wxT("%d"), mConfig.getOcHtdConnectThreshold()));
	mOcHtdDisconnectFilterText->SetValue(wxString::Format(wxT("%u"), mConfig.getOcHtdDisconnectFilter()));

	mFgEnableCheckBox->SetValue(mConfig.getFgEnable());
	mFgChemistryChoice->SetSelection(mConfig.getFgChemistry());
	mFgCellCountText->SetValue(wxString::Format(wxT("%u"), mConfig.getFgCellCount()));

	switch(mFgChemistryChoice->GetSelection())
	{
		case 0:
		case 1:
			switch(mConfig.getFgCellCount())
			{
				case 3: mFgBatteryVoltageChoice->SetSelection(0); break;
				case 6: mFgBatteryVoltageChoice->SetSelection(1); break;
				case 12: mFgBatteryVoltageChoice->SetSelection(2); break;
				case 18: mFgBatteryVoltageChoice->SetSelection(3); break;
				case 24: mFgBatteryVoltageChoice->SetSelection(4); break;
				default: mFgBatteryVoltageChoice->SetSelection(5); break;
			}
			break;


		case 2:
		case 3:
		case 4:
			switch(mConfig.getFgCellCount())
			{
				case 2: mFgBatteryVoltageChoice->SetSelection(0); break;
				case 4: mFgBatteryVoltageChoice->SetSelection(1); break;
				case 8: mFgBatteryVoltageChoice->SetSelection(2); break;
				case 12: mFgBatteryVoltageChoice->SetSelection(3); break;
				case 16: mFgBatteryVoltageChoice->SetSelection(4); break;
				default: mFgBatteryVoltageChoice->SetSelection(5); break;
			}
			break;
	}

	mFgCellCountSizer->ShowItems(mFgBatteryVoltageChoice->GetSelection() == 5);

	mFgCapacityText->SetValue(wxString::Format(wxT("%.1f"), mConfig.getFgDesignCapacity()));

	mFgManualSyncCheckBox->SetValue(mConfig.getFgManualChargeDetectionEnable());
	mFgSyncManualSizer->ShowItems(mFgManualSyncCheckBox->GetValue());

	mFgSyncVoltageThresholdText->SetValue(wxString::Format(wxT("%.3f"), mConfig.getFgSyncVoltageThreshold()));
	mFgSyncCurrentThresholdText->SetValue(wxString::Format(wxT("%.3f"), mConfig.getFgSyncCurrentThreshold()));
	mFgSyncFilterText->SetValue(wxString::Format(wxT("%u"), mConfig.getFgSyncFilter()));
	
	mOcFgDisconnectEnableCheckBox->SetValue(mConfig.getOcFgdDisconnectEnable());
	mOcFgDisconnectThresholdText->SetValue(wxString::Format(wxT("%u"), mConfig.getOcFgdDisconnectThreshold()));
	
	mOcFgConnectEnableCheckBox->SetValue(mConfig.getOcFgdConnectEnable());
	mOcFgConnectThresholdText->SetValue(wxString::Format(wxT("%u"), mConfig.getOcFgdConnectThreshold()));


	mOcGenEnableCheckBox->SetValue(mConfig.getOcGenControlEnable());
	mOcGenVonEnableCheckBox->SetValue(mConfig.getOcGenVonEnable());

	mOcGenVoffEnableCheckBox->SetValue(mConfig.getOcGenVoffEnable());
	mOcGenSoconEnableCheckBox->SetValue(mConfig.getOcGenSoconEnable());
	mOcGenSocoffEnableCheckBox->SetValue(mConfig.getOcGenSocoffEnable());

	mOcGenVonThresholdText->SetValue(wxString::Format(wxT("%.3f"), mConfig.getOcGenVonThreshold()));
	mOcGenVonFilterText->SetValue(wxString::Format(wxT("%u"), mConfig.getOcGenVonFilter()));
	mOcGenVoffThresholdText->SetValue(wxString::Format(wxT("%.3f"), mConfig.getOcGenVoffThreshold()));

	mOcGenSoconThresholdText->SetValue(wxString::Format(wxT("%u"), mConfig.getOcGenSoconThreshold()));
	mOcGenSocoffThresholdText->SetValue(wxString::Format(wxT("%u"), mConfig.getOcGenSocoffThreshold()));

	mOcGenTurnOffDelayText->SetValue(wxString::Format(wxT("%u"), mConfig.getOcGenTurnOffDelay()));

	if (mAdcDisableV2CheckBox->GetValue())
		mOcGenVoltageSourceCheckBox->Hide();

	mOcGenVonSizer->ShowItems(mOcGenVonEnableCheckBox->GetValue());
	mOcGenVoffSizer->ShowItems(mOcGenVoffEnableCheckBox->GetValue());
	mOcGenSoconSizer->ShowItems(mOcGenSoconEnableCheckBox->GetValue());
	mOcGenSocoffSizer->ShowItems(mOcGenSocoffEnableCheckBox->GetValue());

#if defined(_MSC_VER)
	mListBook->GetListView()->SetColumnWidth(0, FromDIP(180));
#endif

	SetSize(FromDIP(wxSize(480, 620)));

	Layout();
	Centre();
}



void GuiDeviceConfig::OnOcGenCheckBoxClicked(wxCommandEvent &event)
{
	mOcGenVonSizer->ShowItems(mOcGenVonEnableCheckBox->GetValue());
	mOcGenVoffSizer->ShowItems(mOcGenVoffEnableCheckBox->GetValue());
	mOcGenSoconSizer->ShowItems(mOcGenSoconEnableCheckBox->GetValue());
	mOcGenSocoffSizer->ShowItems(mOcGenSocoffEnableCheckBox->GetValue());

	mListBook->GetCurrentPage()->Fit();

	wxSize maxSize(0, 0);
	for (size_t i = 0; i < mListBook->GetPageCount(); ++i)
	{
		wxSize pageSize = mListBook->GetPage(i)->GetBestSize();
		maxSize.IncTo(pageSize);
	}

	mListBook->SetMinSize(maxSize);
	Fit();
}


void GuiDeviceConfig::OnDisableV2CheckBoxClicked(wxCommandEvent &event)
{
	if (mAdcDisableV2CheckBox->GetValue())
	{
		mMeterVoltageSourceCheckBox->SetValue(false);
		mOcLvdVoltageSourceCheckBox->SetValue(false);
		mOcHvdVoltageSourceCheckBox->SetValue(false);
		mFgVoltageSourceCheckBox->SetValue(false);
		mOcGenVoltageSourceCheckBox->SetValue(false);

		mMeterVoltageSourceCheckBox->Hide();
		mOcLvdVoltageSourceCheckBox->Hide();
		mOcHvdVoltageSourceCheckBox->Hide();
		mFgVoltageSourceCheckBox->Hide();
		mOcGenVoltageSourceCheckBox->Hide();
	}
	else
	{
		mMeterVoltageSourceCheckBox->Show();
		mOcLvdVoltageSourceCheckBox->Show();
		mOcHvdVoltageSourceCheckBox->Show();
		mFgVoltageSourceCheckBox->Show();
		mOcGenVoltageSourceCheckBox->Show();
	}

	mListBook->GetPage(0)->Fit();
	mListBook->GetPage(1)->Fit();
	mListBook->GetPage(2)->Fit();
	mListBook->GetPage(7)->Fit();
	mListBook->GetPage(8)->Fit();

	wxSize maxSize(0, 0);
	for (size_t i = 0; i < mListBook->GetPageCount(); ++i)
	{
		wxSize pageSize = mListBook->GetPage(i)->GetBestSize();
		maxSize.IncTo(pageSize);
	}

	mListBook->SetMinSize(maxSize);
	Fit();
}


void GuiDeviceConfig::OnManualCheckBoxClicked(wxCommandEvent &event)
{
	mFgSyncManualSizer->ShowItems(mFgManualSyncCheckBox->GetValue());
	
	mListBook->GetCurrentPage()->Fit();

	wxSize maxSize(0, 0);
	for (size_t i = 0; i < mListBook->GetPageCount(); ++i)
	{
		wxSize pageSize = mListBook->GetPage(i)->GetBestSize();
		maxSize.IncTo(pageSize);
	}

	mListBook->SetMinSize(maxSize);
	Fit();

	if (mFgManualSyncCheckBox->GetValue())
		updateFullChargeDetection();
}


void GuiDeviceConfig::OnChoiceBatteryVoltageChanged(wxCommandEvent &event)
{
	updateBatteryCellCount();
	updateFullChargeDetection();

	mListBook->GetCurrentPage()->Fit();

	wxSize maxSize(0, 0);
	for (size_t i = 0; i < mListBook->GetPageCount(); ++i)
	{
		wxSize pageSize = mListBook->GetPage(i)->GetBestSize();
		maxSize.IncTo(pageSize);
	}

	mListBook->SetMinSize(maxSize);
	Fit();
}


void GuiDeviceConfig::OnChoiceShuntChanged(wxCommandEvent &event)
{
	const int32_t selected = mShuntChoice->GetCurrentSelection();
	mMaxShuntCurrentText->SetValue(wxString::Format(wxT("%u"), mConfig.getEngineShuntCurrentMax()));
}


void GuiDeviceConfig::OnChoiceBatteryChemistryChanged(wxCommandEvent &event)
{
	updateBatteryCellCount();
	updateFullChargeDetection();
}


void GuiDeviceConfig::OnTextBatteryCapacityChanged(wxCommandEvent &event)
{
	if (mFgCapacityText->IsModified())
	{
		try
		{
			ParseTextCtrlFloat(mFgCapacityText, wxT("Battery Capacity"));
		}
		catch(ParserException&)
		{
			return;	
		}
	
		updateFullChargeDetection();
	}
}


void GuiDeviceConfig::OnTextBatteryCellsChanged(wxCommandEvent &event)
{
	if (mFgCellCountText->IsModified())
	{
		try
		{
			const uint32_t cells = ParseTextCtrlInt(mFgCellCountText, wxT("Battery Cell Count"));
			checkCellCount(cells, mFgChemistryChoice->GetSelection());
		}
		catch(ParserException &e)
		{
			wxMessageDialog msg(this, e.what(), wxT("Error"), wxOK | wxICON_ERROR);
			msg.ShowModal();
			return;	
		}
	
		updateFullChargeDetection();
	}
}


void GuiDeviceConfig::OnButtonSaveClicked(wxCommandEvent &event)
{
	try
	{
		if (Powermon::hasWifi(mPowermon.getLastDeviceInfo().hardware_revision_bcd))
			mConfig.setWiFiKeepAPOn(mWifiKeepApOnCheckBox->GetValue());

		if (Powermon::hasConfigurableShunt(mPowermon.getLastDeviceInfo().hardware_revision_bcd))
		{
			const int32_t selected = mShuntChoice->GetCurrentSelection();
			if (selected < 0)
				throw ParserException("Shunt not selected");
 
			mConfig.setEngineShuntCurrentNom(l_shunts[selected].current_nom);
			mConfig.setEngineShuntVdropNom(l_shunts[selected].vdrop_nom);
		}

		const uint32_t max_shunt_current = ParseTextCtrlInt(mMaxShuntCurrentText, wxT("Max. Shunt Current"));
		if (max_shunt_current > mConfig.getEngineShuntCurrentNom())
			throw ParserException("Max. Shunt Current cannot be higher than the shunt nominal current");
		mConfig.setEngineShuntCurrentMax(max_shunt_current);

		if (Powermon::hasVoltage2(mPowermon.getLastDeviceInfo().hardware_revision_bcd))
		{
			mConfig.setEngineDisableV2(mAdcDisableV2CheckBox->GetValue());

			mConfig.setEngineMeterVoltageSource(mMeterVoltageSourceCheckBox->GetValue());

			mConfig.setOcLvdSource(mOcLvdVoltageSourceCheckBox->GetValue());
			mConfig.setOcHvdSource(mOcHvdVoltageSourceCheckBox->GetValue());
			mConfig.setFgVoltageSource(mFgVoltageSourceCheckBox->GetValue());
			mConfig.setOcGenVoltageSource(mOcGenVoltageSourceCheckBox->GetValue());
		}

		mConfig.setEngineCurrentSignFlip(mAdcFlipCurrentSignCheckBox->GetValue());
		mConfig.setOcInitialState(mOcInitialStateCheckBox->GetValue());
		mConfig.setOcLatchRelayOn(mOcLatchRelayOnCheckBox->GetValue());
		
		mConfig.setOcInvertOutput(mOcInvertOutputCheckBox->GetValue());

		mConfig.setOcConnectFilter(ParseTextCtrlInt(mOcConnectFilterText, wxT("Connect Filter")));

		mConfig.setMfMode((PowermonConfig::MultiFunctionPinMode)mMfTerminalFunctionChoice->GetCurrentSelection());
		mConfig.setLogMode((PowermonConfig::LogMode)mLoggingModeChoice->GetCurrentSelection());

		mConfig.setOcLvdEnable(mOcLvdEnableCheckBox->GetValue());
		mConfig.setOcLvdDisconnectThreshold(ParseTextCtrlFloat(mOcLvdDisconnectThresholdText, wxT("LVD Disconnect Threshold")));
		mConfig.setOcLvdConnectThreshold(ParseTextCtrlFloat(mOcLvdConnectThresholdText, wxT("LVD Connect Threshold")));
		mConfig.setOcLvdDisconnectFilter(ParseTextCtrlInt(mOcLvdDisconnectFilterText, wxT("LVD Filter")));

		mConfig.setOcOcdEnable(mOcOcdEnableCheckBox->GetValue());
		mConfig.setOcOcdTripThreshold(ParseTextCtrlFloat(mOcOcdTripThresholdText, wxT("OCD Trip Threshold")));
		mConfig.setOcOcdTripFilter(ParseTextCtrlInt(mOcOcdTripFilterText, wxT("OCD Filter")));
		
		mConfig.setOcHvdEnable(mOcHvdEnableCheckBox->GetValue());
		mConfig.setOcHvdDisconnectThreshold(ParseTextCtrlFloat(mOcHvdDisconnectThresholdText, wxT("HVD Disconnect Threshold")));
		mConfig.setOcHvdConnectThreshold(ParseTextCtrlFloat(mOcHvdConnectThresholdText, wxT("HVD Connect Threshold")));
		mConfig.setOcHvdDisconnectFilter(ParseTextCtrlInt(mOcHvdDisconnectFilterText, wxT("HVD Filter")));
		
		if (mOcAutoOffEnableCheckBox->GetValue())
		{
			const uint32_t hours = ParseTextCtrlInt(mOcAutoOffHoursText, wxT("Auto-OFF Timer Hours"));
			const uint32_t minutes = ParseTextCtrlInt(mOcAutoOffMinutesText, wxT("Auto-OFF Timer Minutes"));
			const uint32_t seconds = ParseTextCtrlInt(mOcAutoOffSecondsText, wxT("Auto-OFF Timer Seconds"));

			mConfig.setOcAutoOffTimer(hours * 3600 + minutes * 60 + seconds);
		}
		else
		{
			mConfig.setOcAutoOffTimer(0);
		}

		if (mOcAutoOnEnableCheckBox->GetValue())
		{
			const uint32_t hours = ParseTextCtrlInt(mOcAutoOnHoursText, wxT("Auto-ON Timer Hours"));
			const uint32_t minutes = ParseTextCtrlInt(mOcAutoOnMinutesText, wxT("Auto-ON Timer Minutes"));
			const uint32_t seconds = ParseTextCtrlInt(mOcAutoOnSecondsText, wxT("Auto-ON Timer Seconds"));

			mConfig.setOcAutoOnTimer(hours * 3600 + minutes * 60 + seconds);
		}
		else
		{
			mConfig.setOcAutoOnTimer(0);
		}

		mConfig.setOcLtdEnable(mOcLtdEnableCheckBox->GetValue());
		mConfig.setOcLtdDisconnectThreshold(ParseTextCtrlInt(mOcLtdDisconnectThresholdText, wxT("LTD Disconnect Threshold")));
		mConfig.setOcLtdConnectThreshold(ParseTextCtrlInt(mOcLtdConnectThresholdText, wxT("LTD Connect Threshold")));
		mConfig.setOcLtdDisconnectFilter(ParseTextCtrlInt(mOcLtdDisconnectFilterText, wxT("LTD Filter")));

		mConfig.setOcHtdEnable(mOcHtdEnableCheckBox->GetValue());
		mConfig.setOcHtdDisconnectThreshold(ParseTextCtrlInt(mOcHtdDisconnectThresholdText, wxT("HTD Disconnect Threshold")));
		mConfig.setOcHtdConnectThreshold(ParseTextCtrlInt(mOcHtdConnectThresholdText, wxT("HTD Connect Threshold")));
		mConfig.setOcHtdDisconnectFilter(ParseTextCtrlInt(mOcHtdDisconnectFilterText, wxT("HTD Filter")));

		mConfig.setFgEnable(mFgEnableCheckBox->GetValue());

		const float val = ParseTextCtrlFloat(mFgCapacityText, wxT("Battery Capacity"));
		if ((val < 1.0) || val > 20000)
			throw ParserException("Battery Capacity should be between 1.0Ah and 20,000Ah");
		mConfig.setFgDesignCapacity(val);

		mConfig.setFgChemistry((PowermonConfig::FuelgaugeChemistry)mFgChemistryChoice->GetCurrentSelection());

		const uint32_t cells = ParseTextCtrlInt(mFgCellCountText, wxT("Battery Cell Count"));
		checkCellCount(cells, mFgChemistryChoice->GetSelection());
		mConfig.setFgCellCount(cells);
		
		mConfig.setFgManualChargeDetectionEnable(mFgManualSyncCheckBox->GetValue());
		mConfig.setFgSyncVoltageThreshold(ParseTextCtrlFloat(mFgSyncVoltageThresholdText, wxT("Sync Voltage Threshold")));
		mConfig.setFgSyncCurrentThreshold(ParseTextCtrlFloat(mFgSyncCurrentThresholdText, wxT("Sync Current Threshold")));
		mConfig.setFgSyncFilter(ParseTextCtrlInt(mFgSyncFilterText, wxT("Sync Filter")));

		mConfig.setOcFgdDisconnectEnable(mOcFgDisconnectEnableCheckBox->GetValue());
		uint32_t value = ParseTextCtrlInt(mOcFgDisconnectThresholdText, wxT("Battery Disconnect Threshold"));
		if (value >= 100)
			throw ParserException("Battery Disconnect Threshold should be under 100%");

		mConfig.setOcFgdDisconnectThreshold(value);


		mConfig.setOcFgdConnectEnable(mOcFgConnectEnableCheckBox->GetValue());
		value = ParseTextCtrlInt(mOcFgConnectThresholdText, wxT("Battery Connect Threshold"));
		if (value >= 100)
			throw ParserException("Battery Connect Threshold should be under 100%");

		mConfig.setOcFgdConnectThreshold(value);


		mConfig.setOcGenControlEnable(mOcGenEnableCheckBox->GetValue());

		mConfig.setOcGenVonEnable(mOcGenVonEnableCheckBox->GetValue());
		mConfig.setOcGenVoffEnable(mOcGenVoffEnableCheckBox->GetValue());
		mConfig.setOcGenSoconEnable(mOcGenSoconEnableCheckBox->GetValue());
		mConfig.setOcGenSocoffEnable(mOcGenSocoffEnableCheckBox->GetValue());

		mConfig.setOcGenTurnOffDelay(ParseTextCtrlInt(mOcGenTurnOffDelayText, wxT("Generator Turn Off Delay")));


		if (mOcGenVonEnableCheckBox->GetValue())
		{
			mConfig.setOcGenVonThreshold(ParseTextCtrlFloat(mOcGenVonThresholdText, wxT("Generator Turn On Voltage Threshold")));
			mConfig.setOcGenVonFilter(ParseTextCtrlInt(mOcGenVonFilterText, wxT("Generator Turn On Voltage Filter")));
		}

		if (mOcGenVoffEnableCheckBox->GetValue())
			mConfig.setOcGenVoffThreshold(ParseTextCtrlFloat(mOcGenVoffThresholdText, wxT("Generator Turn Off Voltage Threshold")));

		if (mOcGenSoconEnableCheckBox->GetValue())
			mConfig.setOcGenSoconThreshold(ParseTextCtrlInt(mOcGenSoconThresholdText, wxT("Generator Turn On SoC Threshold")));

		if (mOcGenSocoffEnableCheckBox->GetValue())
			mConfig.setOcGenSocoffThreshold(ParseTextCtrlInt(mOcGenSocoffThresholdText, wxT("Generator Turn Off SoC Threshold")));


		if (Powermon::hasVoltage2(mPowermon.getLastDeviceInfo().hardware_revision_bcd))
		{
			mConfig.setOcLiFePOEnable(mOcLiFePOEnableCheckBox->GetValue());

			const uint32_t cells = ParseTextCtrlInt(mOcLiFePOCellCountText, wxT("Battery Cell Count"));
			checkCellCount(cells, 2);
			mConfig.setOcLiFePOCellCount(cells);

			const float val = ParseTextCtrlFloat(mOcLiFePOBatteryCapacityText, wxT("Battery Capacity"));
			if ((val < 1.0) || val > 20000)
				throw ParserException("Battery Capacity should be between 1.0Ah and 20,000Ah");
			mConfig.setOcLiFePODesignCapacity(val);
		}			
	}
	catch(ParserException &e)
	{
		wxMessageDialog msg(this, e.what(), wxT("Error"), wxOK | wxICON_ERROR);
		msg.ShowModal();
		return;	
	}

	EndModal(wxID_OK);
}


void GuiDeviceConfig::OnOcFeatureEnableCheckBoxClicked(wxCommandEvent &event)
{
	if (mOcLvdEnableCheckBox->GetValue() || 
		mOcHvdEnableCheckBox->GetValue() || 
		mOcOcdEnableCheckBox->GetValue() || 
		mOcAutoOnEnableCheckBox->GetValue() || 
		mOcAutoOffEnableCheckBox->GetValue() || 
		mOcLtdEnableCheckBox->GetValue() || 
		mOcHtdEnableCheckBox->GetValue())
	{
		if (Powermon::hasVoltage2(mPowermon.getLastDeviceInfo().hardware_revision_bcd))
			mOcLiFePOEnableCheckBox->SetValue(false);

		mOcGenEnableCheckBox->SetValue(false);
	}
}


void GuiDeviceConfig::OnOcGenEnableCheckBoxClicked(wxCommandEvent &event)
{
	if (mOcGenEnableCheckBox->GetValue())
	{
		wxMessageDialog msg(this, wxT("Enabling the Generator Control will disable all the other disconnect features.")
									wxT("Please consult the user manual for details on the Generator Control feature."), wxT("Warning"), wxOK | wxICON_WARNING);
		msg.ShowModal();

		mOcLvdEnableCheckBox->SetValue(false);
		mOcHvdEnableCheckBox->SetValue(false);
		mOcOcdEnableCheckBox->SetValue(false);
		mOcAutoOnEnableCheckBox->SetValue(false);
		mOcAutoOffEnableCheckBox->SetValue(false);
		mOcLtdEnableCheckBox->SetValue(false);
		mOcHtdEnableCheckBox->SetValue(false);

		if (Powermon::hasVoltage2(mPowermon.getLastDeviceInfo().hardware_revision_bcd))
			mOcLiFePOEnableCheckBox->SetValue(false);
	}
}


void GuiDeviceConfig::OnOcLiFePOEnableCheckBoxClicked(wxCommandEvent &event)
{
	if (mOcLiFePOEnableCheckBox->GetValue())
	{
		wxMessageDialog msg(this, wxT("Enabling the LiFePO charge manager will disable all the other features, including the fuel gauge.")
									wxT("Please consult the user manual for details on the LiFePO Charge Manager feature."), wxT("Warning"), wxOK | wxICON_WARNING);
		msg.ShowModal();

		mOcLvdEnableCheckBox->SetValue(false);
		mOcHvdEnableCheckBox->SetValue(false);
		mOcOcdEnableCheckBox->SetValue(false);
		mOcAutoOnEnableCheckBox->SetValue(false);
		mOcAutoOffEnableCheckBox->SetValue(false);
		mOcLtdEnableCheckBox->SetValue(false);
		mOcHtdEnableCheckBox->SetValue(false);
		mFgEnableCheckBox->SetValue(false);
		mOcGenEnableCheckBox->SetValue(false);
	}
}


void GuiDeviceConfig::OnClose(wxCloseEvent &event)
{
	Hide();
}


void GuiDeviceConfig::updateBatteryCellCount(void)
{
	const int32_t battery_voltage_selection = mFgBatteryVoltageChoice->GetSelection();
	if (battery_voltage_selection < 5)
	{
		uint8_t cell_count = 0;
		switch(mFgChemistryChoice->GetSelection())
		{
			case 0:	//Lead chemistries
			case 1:
				switch(battery_voltage_selection)
				{
					case 0: cell_count = 3; break;
					case 1: cell_count = 6; break;
					case 2: cell_count = 12; break;
					case 3: cell_count = 18; break;
					case 4: cell_count = 24; break;
				}
				break;


			case 2: //Li chemistries
			case 3:
			case 4:
				switch(battery_voltage_selection)
				{
					case 0: cell_count = 2; break;
					case 1: cell_count = 4; break;
					case 2: cell_count = 8; break;
					case 3: cell_count = 12; break;
					case 4: cell_count = 16; break;
				}
				break;
		}

		mFgCellCountText->SetValue(wxString::Format(wxT("%u"), cell_count));
		mFgCellCountSizer->ShowItems(false);
	}
	else
	{
		mFgCellCountSizer->ShowItems(true);
	}
}


void GuiDeviceConfig::updateFullChargeDetection(void)
{
	const uint32_t cells = ParseTextCtrlInt(mFgCellCountText, wxT("Battery Cell Count"));
	checkCellCount(cells, mFgChemistryChoice->GetSelection());

	const float capacity = ParseTextCtrlFloat(mFgCapacityText, wxT("Battery Capacity"));
	if ((capacity < 1.0) || capacity > 20000)
		throw ParserException("Battery Capacity should be between 1.0Ah and 20,000Ah");

	float voltage_threhold = 0;
	switch(mFgChemistryChoice->GetSelection())
	{
		case 0:
		case 1: voltage_threhold = cells * 2.35; break;
		case 2: voltage_threhold = cells * 3.5; break;
		case 3:
		case 4: voltage_threhold = cells * 4.1; break;
	}

	mFgSyncVoltageThresholdText->SetValue(wxString::Format(wxT("%.3f"), voltage_threhold));
	mFgSyncCurrentThresholdText->SetValue(wxString::Format(wxT("%.3f"), capacity / 40));
	mFgSyncFilterText->SetValue(wxT("600"));
}


void GuiDeviceConfig::OnChoiceLiFePOBatteryVoltageChanged(wxCommandEvent &event)
{
	const int32_t battery_voltage_selection = mOcLiFePOBatteryVoltageChoice->GetSelection();
	if (battery_voltage_selection < 5)	
	{
		uint8_t cell_count = 0;
		switch(battery_voltage_selection)
		{
			case 0: cell_count = 2; break;
			case 1: cell_count = 4; break;
			case 2: cell_count = 8; break;
			case 3: cell_count = 12; break;
			case 4: cell_count = 16; break;
		}

		mOcLiFePOCellCountText->SetValue(wxString::Format(wxT("%u"), cell_count));
		mOcLiFePOCellCountSizer->ShowItems(false);
	}
	else
	{
		mOcLiFePOCellCountSizer->ShowItems(true);
	}

	mListBook->GetCurrentPage()->Fit();

	wxSize maxSize(0, 0);
	for (size_t i = 0; i < mListBook->GetPageCount(); ++i)
	{
		wxSize pageSize = mListBook->GetPage(i)->GetBestSize();
		maxSize.IncTo(pageSize);
	}

	mListBook->SetMinSize(maxSize);
	Fit();
}


void GuiDeviceConfig::OnTextLiFePOCellsChanged(wxCommandEvent &event)
{
	if (mOcLiFePOCellCountText->IsModified())
	{
		try
		{
			const uint32_t cells = ParseTextCtrlInt(mOcLiFePOCellCountText, wxT("Battery Cell Count"));
			checkCellCount(cells, 2);
		}
		catch(ParserException &e)
		{
			wxMessageDialog msg(this, e.what(), wxT("Error"), wxOK | wxICON_ERROR);
			msg.ShowModal();
			return;	
		}
	}
}



BEGIN_EVENT_TABLE(GuiDeviceConfig, wxDialog)
EVT_CLOSE(GuiDeviceConfig::OnClose)

EVT_CHECKBOX(ID_CHECKBOX_DISABLE_V2, GuiDeviceConfig::OnDisableV2CheckBoxClicked)
EVT_CHECKBOX(ID_CHECK_MANUAL_DETECTION, GuiDeviceConfig::OnManualCheckBoxClicked)
EVT_CHECKBOX(ID_CHECK_VON_GENERATOR, GuiDeviceConfig::OnOcGenCheckBoxClicked)
EVT_CHECKBOX(ID_CHECKBOX_ENABLE_FEATURE, GuiDeviceConfig::OnOcFeatureEnableCheckBoxClicked)
EVT_CHECKBOX(ID_CHECKBOX_ENABLE_GENERATOR, GuiDeviceConfig::OnOcGenEnableCheckBoxClicked)
EVT_CHECKBOX(ID_CHECKBOX_ENABLE_LIFEPO_CM, GuiDeviceConfig::OnOcLiFePOEnableCheckBoxClicked)

EVT_CHOICE(ID_CHOICE_SHUNT, GuiDeviceConfig::OnChoiceShuntChanged)
EVT_CHOICE(ID_CHOICE_FG_VOLTAGE, GuiDeviceConfig::OnChoiceBatteryVoltageChanged)
EVT_CHOICE(ID_CHOICE_FG_CHEMISTRY, GuiDeviceConfig::OnChoiceBatteryChemistryChanged)
EVT_CHOICE(ID_CHOICE_LIFEPO_CM_VOLTAGE, GuiDeviceConfig::OnChoiceLiFePOBatteryVoltageChanged)

EVT_TEXT(ID_TEXT_FG_CAPACITY, GuiDeviceConfig::OnTextBatteryCapacityChanged)
EVT_TEXT(ID_TEXT_FG_CELLS, GuiDeviceConfig::OnTextBatteryCellsChanged)
EVT_TEXT(ID_CHOICE_LIFEPO_CM_CELLS, GuiDeviceConfig::OnTextLiFePOCellsChanged)




EVT_BUTTON(wxID_OK, GuiDeviceConfig::OnButtonSaveClicked)

END_EVENT_TABLE()