add close app handling

main
Yik Teng Hie 2 years ago
parent aaba048d74
commit 6f78bc60a9

@ -80,28 +80,14 @@ struct stPackageInfo
// Light Config
typedef struct _LIGHT_CONFIG {
int iRed;
int iAmber;
int iGreen;
int iBuzzer;
int iLightOnTime;
int iLightOffTime;
int iBuzzerOnTime;
int iBuzzerOffTime;
// ctor
_LIGHT_CONFIG()
{
iRed = 1;
iAmber = 1;
iGreen = 1;
iBuzzer = 0;
iLightOnTime = 500;
iLightOffTime = 500;
iBuzzerOnTime = 500;
iBuzzerOffTime = 500;
};
int iRed{ 1 };
int iAmber{ 1 };
int iGreen{ 1 };
int iBuzzer{ 0 };
int iLightOnTime{ 500 };
int iLightOffTime{ 500 };
int iBuzzerOnTime{ 500 };
int iBuzzerOffTime{ 500 };
} LIGHT_CONFIG;

@ -26,6 +26,8 @@
#define WM_ON_SEND_SECGEM_EVENT (WM_USER + WM_USR_DEF_GAP + 19) // 2043
// ems status
#define WM_ON_EMS_STATUS (WM_USER + WM_USR_DEF_GAP + 20) // 2044
// App Close request from WPF UI
#define WM_ON_APP_CLOSE_REQ (WM_USER + WM_USR_DEF_GAP + 21) // 2045
//
#define WM_ON_REQUEST_SEND_MESSAGE_TO_ALL_PROPERTY_PAGES (WM_USER + WM_USR_DEF_GAP + 30) // 2054

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -4,6 +4,8 @@
#pragma once
#include <array>
#include "cmnhdr.h"
#include "RunTask.h"
#include "Input.h"
@ -23,7 +25,7 @@ class MCCTRDLLCLASS CCheckSwitch : public CRunTask
{
public:
bool SetOuputState(int nOutput, bool bState);
void DoorLock(bool bLock = TRUE);
void DoorLock(bool bLock = true);
bool IsEngineeringBypass();
bool IsCurtainBypass();
bool IsDoorBypass();
@ -31,9 +33,9 @@ public:
bool IsHardwareBypass();
bool IsStopButtonActivated();
void InitParameters();
void BypassEngineering(bool bBypass = TRUE);
void BypassCurtain(bool bBypass = TRUE);
void BypassDoor(bool bBypass = TRUE);
void BypassEngineering(bool bBypass = true);
void BypassCurtain(bool bBypass = true);
void BypassDoor(bool bBypass = true);
void SetSystemOutput(int nOutput, COutput* copOutputPt);
CCheckSwitch(CRunCtrl* pRunCtrl);
@ -57,10 +59,10 @@ private:
void GetMainApp();
void CheckSwitch(int nSwitchNo, CInput* cipSwitch, stMSG_SEND* pMsgSend);
bool m_bSafetySwitch[SWITCH_SYSTEM];
CInput* m_cipSwitch[SWITCH_SYSTEM];
std::array<bool, SWITCH_SYSTEM> m_bSafetySwitch;
std::array<CInput*, SWITCH_SYSTEM> m_cipSwitch;
stMSG_SEND m_stMsgSend[SWITCH_SYSTEM];
std::array < stMSG_SEND, SWITCH_SYSTEM> m_stMsgSend;
COutput* m_copSystemDoor;
COutput* m_copCurtainSensorReset;

@ -3,6 +3,7 @@
//////////////////////////////////////////////////////////////////////
#pragma once
#include <array>
#include <string>
#include "TimeEquipSubState.h"
@ -131,9 +132,9 @@ protected:
// CTimeEquipSubState *m_ctesEquipSubState;
int m_iEvt[MAX_EQUIP_SUB_STATE];
std::array<int, MAX_EQUIP_SUB_STATE> m_iEvt;
CTimeEquipSubState m_ctesEquipSubState[MAX_EQUIP_SUB_STATE];
std::array < CTimeEquipSubState, MAX_EQUIP_SUB_STATE> m_ctesEquipSubState;
private:
// prevent accidental copy

@ -14,8 +14,8 @@ class MCCTRDLLCLASS CRunTask
{
public:
DWORD GetThreadPriority();
BOOL SetThreadPriority(int nPriority);
BOOL IsTerminate(void);
bool SetThreadPriority(int nPriority);
bool IsTerminate(void);
CRunTask();
virtual ~CRunTask();

@ -13,44 +13,27 @@
//
typedef struct _stNetAddress
{
std::string strIpAddress;
int nPort;
// ctor
_stNetAddress()
{
strIpAddress = "127.0.0.1";
nPort = 4000;
}
std::string strIpAddress{ "127.0.0.1" };
int nPort{ 400 };
} stNetAddress;
//
typedef struct _stComSetting
{
std::string strCommPort;
long lBaudRate;
std::string strParity;
int nDataBits;
double dStopBits;
// ctor
_stComSetting()
{
strCommPort = "COM1";
lBaudRate = 9600;
strParity = "None";
nDataBits = 8;
dStopBits = 1.0;
}
std::string strCommPort{ "COM1" };
long lBaudRate{ 9600 };
std::string strParity{ "None" };
int nDataBits{ 8 };
double dStopBits{ 1.0 };
} stComSetting;
class MCCTRDLLCLASS CRunComm : public CRun
{
public:
int GetPortNo(bool bMaint = FALSE, int nWhichOne = 0);
std::string GetIpAddress(bool bMaint = FALSE, int nWhichOne = 0);
CRunComm(int nID, std::string strName, std::vector<struct stModule>& modules, int* iSequenceModuleNo = NULL);
int GetPortNo(bool bMaint = false, int nWhichOne = 0);
std::string GetIpAddress(bool bMaint = false, int nWhichOne = 0);
CRunComm(int nID, std::string strName, std::vector<struct stModule>& modules, std::vector<int>& iSequenceModuleNo);
virtual ~CRunComm();
enum
@ -71,11 +54,11 @@ protected:
bool IsMaintenanceDisconnectPort();
bool IsMaintenanceConnectPort();
bool IsMaintenanceSendPacket();
double GetStopBits(bool bMaint = FALSE, int nWhichOne = 0);
int GetDataBits(bool bMaint = FALSE, int nWhichOne = 0);
long GetBaudRate(bool bMaint = FALSE, int nWhichOne = 0);
std::string GetParity(bool bMaint = FALSE, int nWhichOne = 0);
std::string GetCommPort(bool bMaint = FALSE, int nWhichOne = 0);
double GetStopBits(bool bMaint = false, int nWhichOne = 0);
int GetDataBits(bool bMaint = false, int nWhichOne = 0);
long GetBaudRate(bool bMaint = false, int nWhichOne = 0);
std::string GetParity(bool bMaint = false, int nWhichOne = 0);
std::string GetCommPort(bool bMaint = false, int nWhichOne = 0);
bool GetCommPortSetting(std::string csIniFile, std::string csSectionName, int nWhichOne = 0);
bool GetNetworkPortSetting(std::string csIniFile, std::string csSectionName, int nWhichOne = 0);
// send com data to modules in maintenance mode

@ -107,7 +107,7 @@ public:
//
virtual void OnMcStopped();
//
void GetModuleEnable(std::string csFilename, std::string csSectionName, std::string csKeyName);
void GetModuleEnable(const std::string& csFilename, const std::string& csSectionName, const std::string& csKeyName);
bool IsModuleEnable();
int GetMaintenanceMoveProfile();
void MaintenanceCheckMotorComplete(); // request to start check maintenance motor move complete
@ -116,25 +116,25 @@ public:
void EndCloseLot(void);
void OnCloseLot(void);
void CloseLotComplete(void);
BOOL IsCloseLot(void);
BOOL IsCloseLotComplete(void);
bool IsCloseLot(void);
bool IsCloseLotComplete(void);
// reset close lot event
void ResetCloseLot();
void Warning(int nID, BOOL bDisplay = FALSE);
BOOL GetCountJam(void);
void Warning(int nID, bool bDisplay = false);
bool GetCountJam(void);
void PowerUpRestart(void);
void AutoRestart(void);
BOOL IsJamCode(void);
bool IsJamCode(void);
int GetWarning(void);
int GetJam(void);
BOOL IsWarning(void);
BOOL IsPowerUpComplete(void);
bool IsWarning(void);
bool IsPowerUpComplete(void);
void PowerUpComplete(void);
virtual void InitParameters(void);
CRun(int nID, std::string strName, std::vector<struct stModule>& modules, int* iSequenceModuleNo = NULL);
CRun(int nID, const std::string& strName, const std::vector<struct stModule>& modules, const std::vector<int>& sequenceModuleNo);
virtual ~CRun();
// gary 05jun03
@ -148,19 +148,19 @@ public:
return m_nModuleId;
}
virtual BOOL PowerUpOperation(void);
virtual BOOL TaskOperation(void);
virtual BOOL AutoOperation(void);
virtual BOOL MaintenanceOperation(void);
virtual bool PowerUpOperation(void);
virtual bool TaskOperation(void);
virtual bool AutoOperation(void);
virtual bool MaintenanceOperation(void);
void OnPowerUp(void);
void OnStop(void);
void OnJamReset(void);
void OnAuto(void);
void OnTask(void);
BOOL IsStop(void);
BOOL IsJam(void);
BOOL GetWarningClear(void);
bool IsStop(void);
bool IsJam(void);
bool GetWarningClear(void);
// get warning code
int GetWarningCode();
@ -227,7 +227,7 @@ public:
void ClearAllNoMaterial();
// check if jam has not been registered before
BOOL IsJamNotRegistered();
bool IsJamNotRegistered();
// open lot has been trigger by GUI
virtual void OnOpenLot();
@ -242,13 +242,13 @@ public:
void ClearAutoRestart();
// check if machine has restart
BOOL IsAutoRestart();
bool IsAutoRestart();
// clear power up restart
void ClearPowerUpRestart();
// check if machine has restart
BOOL IsPowerUpRestart();
bool IsPowerUpRestart();
// send com data to modules in maintenance mode
virtual void OnMaintenanceSendCom(LPCTSTR lpcComPort, int iBaudRate, int iDataBits,
@ -319,9 +319,9 @@ public:
void ClrWarn();
protected:
std::vector<stModule>& m_modules;
HWND m_hwndMainApp;
int* m_iSequenceModuleNo; // integer array to keep track which module belong to which sequence
const std::vector<stModule>& m_modules;
HWND m_hwndMainApp{nullptr};
const std::vector<int>& m_sequenceModuleNo; // integer array to keep track which module belong to which sequence
// get the module ID
int GetModuleId();
@ -370,14 +370,14 @@ private:
std::set<int> m_setWarn;
// Gary 03jun03 start
BOOL m_bWarningClear;
BOOL m_bCountJam;
BOOL m_bJamCode;
BOOL m_bWarning;
BOOL m_bPowerUpComplete;
BOOL m_bJamflag;
BOOL m_bAutoRestart;
BOOL m_bPowerUpRestart;
bool m_bWarningClear;
bool m_bCountJam;
bool m_bJamCode;
bool m_bWarning;
bool m_bPowerUpComplete;
bool m_bJamflag;
bool m_bAutoRestart;
bool m_bPowerUpRestart;
int m_nMode;
int m_nPrevMode;
int m_nState;
@ -392,7 +392,7 @@ private:
int m_state;
std::string m_strName;
BOOL m_bCloseLotComplete;
bool m_bCloseLotComplete;
bool m_bMaintenanceHmMtr; // true = Home Motor in maintenance mode

@ -9,6 +9,7 @@
// For Testing
#pragma once
#include <array>
#include <vector>
#include <string>
#include <functional>
@ -54,6 +55,9 @@
#define CMD_JOG _T("JOG")
#define CMD_MOTOR _T("MOTOR")
// close app
#define CMD_CLOSEAPP ("CLOSEAPP")
#define CMD_COM _T("COM")
#define CMD_TCP _T("TCP")
@ -118,6 +122,9 @@ enum TCP_PARAMS
#define STRING_MSG_TASKMODE _T("TASKMODE")
#define STRING_MSG_TASKMODE_RESET _T("TASKMODE_RESET")
#define STRING_MSG_POSTWINMSG ("POSTWINMSG") // format "POSTWINMSG:1000,1,1" or "POSTWINMSG:winMsg,wp,lp"
#define NUM_PARAM_POSTWINMSG 3
#define DEFAULT_MSG_PORT "7468"
enum MACHINE_MSG_TYPE
{
@ -175,7 +182,7 @@ const std::string TLightState[MAX_LIGHT_MAX_STATE]{
class MCCTRDLLCLASS CRunCtrl : public CRunTask
{
public:
CRunCtrl(std::vector<std::unique_ptr<CRun>>& pRuns);
CRunCtrl(const std::vector<std::unique_ptr<CRun>>& pRuns, const std::vector<int>& sequenceModuleNo);
~CRunCtrl();
protected:
@ -239,16 +246,16 @@ public:
bool IsDoorOpen();
void SetSafetySwitch(int nSwitch, bool bSafety);
void BypassDoor(BOOL bState);
void BypassCurtain(BOOL bState);
void BypassDoor(bool bState);
void BypassCurtain(bool bState);
void SetSystemOutput(int nOutput, COutput* pOutput);
bool IsEngineeringBypass();
bool IsCurtainBypass();
bool IsDoorBypass();
void SetSystemInput(int nSwitch, CInput* pInput);
void ResetPowerUpComplete(void);
void SetModuleWarning(BOOL bState);
void SetSystemWarning(BOOL bState);
void SetModuleWarning(bool bState);
void SetSystemWarning(bool bState);
void SetIndicator(int nIndicator, COutput* output);
void GetLightStatus(std::vector<int>& vecLight);
void InitParameters(void);
@ -303,10 +310,10 @@ public:
// Utilities
int GetMcState();
BOOL IsModified();
void DisplayMsg(CDC* pDC, std::string& msg, int xOff, int yOff, BOOL debug = false, BOOL debug_jam = false);
bool IsModified();
void DisplayMsg(CDC* pDC, std::string& msg, int xOff, int yOff, bool debug = false, bool debug_jam = false);
BOOL IsAllPowerUp(void);
bool IsAllPowerUp(void);
// open lot
void OpenLot();
@ -388,7 +395,7 @@ public:
void SaveLotInfo(LPCTSTR lpLotNo, LPCTSTR lpCurTime);
// get Lot record semie10 timing
StSemiE10 GetLotRecord(std::string csLotNo, std::string csLotStartTime);
StSemiE10 GetLotRecord(const std::string& csLotNo, const std::string& csLotStartTime);
// Delete lot record
void DeleteLotRecord(std::string csLotNumber, double dStartDateTime);
@ -442,11 +449,10 @@ public:
void MaintenanceDisconnectPort(LPCTSTR IpAddress, int iPortNo);
// setup SeqRun Module Mapping
void SetSequenceModuleNo(const std::vector<int>& vecSeqModuleNo);
int GetSequenceModuleNo(int nModuleIndex);
// find Gui Window Handler
void FindGuiWindow(CWnd* pMainWnd = NULL);
void FindGuiWindow(CWnd* pMainWnd = nullptr);
// Gui Separator
void InitGuiSeparator(void);
@ -465,9 +471,9 @@ public:
// Attributes
int m_mcState;
BOOL m_bDiagnose;
bool m_bDiagnose;
BOOL* m_pbMaintenanceMode;
bool* m_pbMaintenanceMode;
enum
{
@ -565,15 +571,15 @@ private:
void InitTowerlightConfig();
void InitAuthorizationLevel();
void InitDisableMessageBuzzer();
BOOL IsCloseLotComplete(void);
BOOL IsWarning(void);
bool IsCloseLotComplete(void);
bool IsWarning(void);
// Operations
void JamResetAllRun(void);
void RestartAllRun();
void SuspendAllRun();
bool ResumeAllRun();
inline void SetMcState(int state);
BOOL IsJam();
bool IsJam();
// Run Time Operations
void StartRunTime();
@ -583,13 +589,13 @@ private:
void OnMcStopped();
// Machine State
BOOL CheckMachineState();
bool CheckMachineState();
void SaveMachineState();
void TaskProcessRxMsgFn();
void TaskTcpIPServerFn();
std::string ConnectionString(std::string strDbName);
std::string ConnectionString(const std::string& strDbName);
void EndAllRun(void);
@ -669,15 +675,15 @@ private:
CMutex m_mcCtrlMutex; // mutex to synchronised machine control
// run modules
std::vector<std::unique_ptr<CRun>>& m_pRuns;
const std::vector<std::unique_ptr<CRun>>& m_pRuns;
BOOL m_bCloseLot;
bool m_bCloseLot;
LIGHT_CONFIG m_lightConfig[NUM_MC_STATE];
LIGHT_CONFIG m_customLightCfg[CUSTOM_LIGHT_MAX];
std::array<LIGHT_CONFIG, NUM_MC_STATE> m_lightConfig;
std::array<LIGHT_CONFIG, CUSTOM_LIGHT_MAX> m_customLightCfg;
LIGHT_CONFIG m_currentLightStatus;
BOOL m_bBlink;
bool m_bBlink;
COutput* m_copLightRed;
COutput* m_copLightAmber;
@ -688,8 +694,8 @@ private:
COutput* m_copStopIndicator;
COutput* m_copResetIndicator;
BOOL m_bSystemWarning;
BOOL m_bModuleWarning;
bool m_bSystemWarning;
bool m_bModuleWarning;
bool m_bResetMaintMode; // flag to detemine reset maintenance mode
bool m_bMaintMode; // flag to determine set to maintenance mode
@ -704,7 +710,7 @@ private:
// Gary 24Mar10, V1.13.4, change this object from private to public
// CSemiE10RS m_SemiE10;
bool m_bMcCtrl[MAX_MC_CTRL]; // flag to determine machine control
std::array<bool, MAX_MC_CTRL> m_bMcCtrl; // flag to determine machine control
// flag to determine whether no material warning has been post to GUI before
// true = post no material warning before
@ -835,7 +841,7 @@ private:
// add capability - in order to share with ui code using shm design, unable to use vector
// TODO: to review shm if needed
std::vector<int> m_vecSequenceModuleNo;
const std::vector<int>& m_vecSequenceModuleNo;
// to store list of callback
std::function<void(void)> m_fnCallback[MAX_CALLBACK_FN];

Binary file not shown.

Binary file not shown.

@ -19,33 +19,33 @@ public:
// Get FileName
LPCTSTR GetFileName();
BOOL WriteString(LPCTSTR lpKeyName, LPCTSTR lpString);
BOOL WriteLong(LPCTSTR lpKeyName, long lLong);
BOOL WriteDouble(LPCTSTR lpKeyName, double dDouble);
std::string GetString(LPCTSTR lpKeyName, LPCTSTR lpDefaultString, BOOL bCreateIfFail = TRUE);
long GetLong(LPCTSTR lpKeyName, long lDefaultLong, BOOL bCreateIfFail = TRUE);
double GetDouble(LPCTSTR lpKeyName, double dDefaultDouble, BOOL bCreateIfFail = TRUE);
bool GetBool(LPCTSTR lpKeyName, LPCTSTR lpDefaultString, BOOL bCreateIfFail = TRUE);
bool WriteString(LPCTSTR lpKeyName, LPCTSTR lpString);
bool WriteLong(LPCTSTR lpKeyName, long lLong);
bool WriteDouble(LPCTSTR lpKeyName, double dDouble);
std::string GetString(LPCTSTR lpKeyName, LPCTSTR lpDefaultString, bool bCreateIfFail = true);
long GetLong(LPCTSTR lpKeyName, long lDefaultLong, bool bCreateIfFail = true);
double GetDouble(LPCTSTR lpKeyName, double dDefaultDouble, bool bCreateIfFail = true);
bool GetBool(LPCTSTR lpKeyName, LPCTSTR lpDefaultString, bool bCreateIfFail = true);
// write longlong value to file
BOOL WriteLongLong(LPCTSTR lpKeyName, LONGLONG lLong);
bool WriteLongLong(LPCTSTR lpKeyName, LONGLONG lLong);
// get longlong value from file
LONGLONG GetLongLong(LPCTSTR lpKeyName, LONGLONG lDefaultLong, BOOL bCreateIfFail = TRUE);
LONGLONG GetLongLong(LPCTSTR lpKeyName, LONGLONG lDefaultLong, bool bCreateIfFail = true);
// write longlong value to file
static BOOL WriteLongLong(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, LONGLONG lLong);
static bool WriteLongLong(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, LONGLONG lLong);
// get long long value from file
static LONGLONG GetLongLong(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, LONGLONG lDefaultLong, BOOL bCreateIfFail = TRUE);
static BOOL WriteString(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, LPCTSTR lpString);
static BOOL WriteLong(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, long lLong);
static BOOL WriteDouble(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, double dDouble);
static std::string GetString(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, LPCTSTR lpDefaultString, BOOL bCreateIfFail = TRUE);
static long GetLong(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, long lDefaultLong, BOOL bCreateIfFail = TRUE);
static double GetDouble(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, double dDefaultDouble, BOOL bCreateIfFail = TRUE);
static bool GetBool(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, LPCTSTR lpDefaultString, BOOL bCreateIfFail = TRUE);
static LONGLONG GetLongLong(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, LONGLONG lDefaultLong, bool bCreateIfFail = true);
static bool WriteString(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, LPCTSTR lpString);
static bool WriteLong(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, long lLong);
static bool WriteDouble(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, double dDouble);
static std::string GetString(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, LPCTSTR lpDefaultString, bool bCreateIfFail = true);
static long GetLong(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, long lDefaultLong, bool bCreateIfFail = true);
static double GetDouble(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, double dDefaultDouble, bool bCreateIfFail = true);
static bool GetBool(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, LPCTSTR lpDefaultString, bool bCreateIfFail = true);
// attributes
private:

@ -27,8 +27,8 @@ public:
LPVOID Open(DWORD dwTimeout = INFINITE); // SM must be opened before use. The pointer
// returns must be type casted to it type
// Returns NULL if the SM was not created ot failed
BOOL Close(); // SM should be closed after use so that others can open the SM
BOOL IsOpen()
bool Close(); // SM should be closed after use so that others can open the SM
bool IsOpen()
{
return m_bOpen;
} // Check if SM is opened
@ -52,7 +52,7 @@ private:
HANDLE m_hMutex;
LPVOID m_pvData;
HANDLE m_hMapping;
BOOL m_bOpen;
bool m_bOpen;
DWORD m_dwLength;
bool m_bUnMappedOnDeleted;
};

@ -16,7 +16,7 @@ public:
void InsertTrigger(HANDLE handle);
// void InsertInput(CInput* pInput, BOOL bState, DWORD dwDebounce, DWORD dwTimeout);
DWORD Wait(DWORD dwTimeOut = INFINITE);
BOOL WaitForAll(DWORD dwTimeOut = INFINITE);
bool WaitForAll(DWORD dwTimeOut = INFINITE);
void Reset();
protected:

Binary file not shown.

Binary file not shown.
Loading…
Cancel
Save