#include "stdafx.h"
#pragma unmanaged
#include "EpdBase.h"
#include "TimeFunctions.h"
#define DiscoveryTimeslotPeriod 500
#define DiscoveryExpirationPeriod  6000
#define RxRetryCount 5
#define TxRetryCount 5
#pragma unmanaged
EpdBase::Epd2U::Epd2U(int comport)
{
	port = comport;
	CommsInitialized = 0;
	isBG = isNG = VersionOK = hasAlarm = false;
	maxDiscoverTime = 10000;
	hWaitTimer = CreateWaitableTimer(NULL, TRUE, NULL);
	if (port == 0) return;
	SetAutoCommit(0);
	Error = 123;
	//SetTimeouts(port, 20, 1000, 1000);
}
WORD EpdBase::Epd2U::WriteConfig()
{
	if (port == 0) return 1;
	return WriteDisplayConfig(00,2,40,10, 2,displayConfig, 56);
}
DWORD EpdBase::Epd2U::StartCom()
{
	DWORD rv;
	ErrorReason = 0;
	int mx = 10;
	if (port == 0) {
		CommsInitialized = 1;
		return 1;
	}
	if (CommsInitialized==0)
		do {
			InitRv = InitComms(port, 9600, 8, 0, 0);
			if (InitRv == 0) {
				rv= GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);
				Sleep(300); DeinitComms(port);
			}
			else {
				SetRetryCounts(RxRetryCount, TxRetryCount);
				SetDiscoveryTimeslotPeriod(port, DiscoveryTimeslotPeriod);
				SetDiscoveryTimeslotCount(port, 1);
				SetDiscoveryExpirationPeriod(port, DiscoveryExpirationPeriod);
			}
		} while (InitRv == 0 && mx > 0);
	
	CommsInitialized = InitRv;
	isBG = isNG = VersionOK = hasAlarm = false;
	return ErrorReason;
}

void EpdBase::Epd2U::EndCom()
{
	if (port > 0 && CommsInitialized==1) DeinitComms(port);
	CommsInitialized = 0;
}

EpdBase::Epd2U::~Epd2U()
{
	EndCom();
}
int EpdBase::Epd2U::StartDiscover( int EpdTimeout)
{
	maxDiscoverTime = EpdTimeout;
	if (port == 0) {
		DeviceCount = 1;
		DeviceIDs[0] = 768;
		return 1;
	} 
	DiscRV = 0;
	ConnectTries = 0; ConnectTimerFired = 0;
	if (CommsInitialized == 1) {
		FlushDiscoveryCache(port);
		FlushBuffers(port);
		Error = 0; 
		int maxtries = 100; DeviceCount = 0; DiscRV = 1; DWORD wo = 1;  // ongeveer 43 tries in 10 seconden
		LARGE_INTEGER wt; //100 nanoseconds *10000 = millisec
		//union { LARGE_INTEGER li; FILETIME ft; LONGLONG ll; } t1, t2;
		//wt.QuadPart = -300000000LL;
		wt.QuadPart = Int32x32To64(maxDiscoverTime, -10000); //10 seconden proberen
		if (hWaitTimer != nullptr) 
			SetWaitableTimer(hWaitTimer, &wt, 0, NULL, NULL, 0);
		//GetSystemTimeAsFileTime(&t1.ft);
		while (DeviceCount == 0 && wo != 0) //tries-- > 0 && 
		{
			ConnectTries++;
			DiscRV = Discover(port, DeviceIDs, MaxDevices, &DeviceCount);
			if (DiscRV == 0) {
				Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);
			}
			wo = WaitForSingleObject(hWaitTimer, 100);
			if (wo == 0) {ConnectTimerFired = 1; DiscRV = 1; }
		}
		//GetSystemTimeAsFileTime(&t2.ft);
		CancelWaitableTimer(hWaitTimer);
	}
	return DiscRV;
}

/// <summary>
/// 
/// </summary>
/// <param name="DeviceID"></param>
/// <returns>0 OK</returns>
int EpdBase::Epd2U::Open(DWORD DeviceID)
{
	int rv = 0;
	CurrentDeviceID = DeviceID;
	if (port > 0) {
		ConRV = Connect(DeviceID);
		if (ConRV == 0) Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);
		else {
			OpenRV = OpenDevice(port, CurrentDeviceID);
			if (OpenRV == 0) rv=Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);
		}
	}
	else { OpenRV = ConRV = 1; rv = 0; }
	return rv;
}

void EpdBase::Epd2U::Close()
{
	if (port > 0)	CloseDevice();
	CurrentDeviceID = 0;
}

int  EpdBase::Epd2U::CloseAndSignal(WORD EndSessionControl, WORD duration)
{
	int rv = 0;
	WORD r;
	Sleep(250);
	r = EndSession(EndSessionControl, duration);
	if (r == 1) rv = 0;  else rv = 1;
	Close();
	return rv;
}


void EpdBase::Epd2U::DecodeFunctionFlags()
{
	EpdType = (FunctionFlags & 0x1F00) >> 8;
	//0=BG 3=Neutron  4=BG Mk3 7=Neutron Mk4
}
void EpdBase::Epd2U::DecodeStatus()
{
	DetectorsOn = (OperatingStatus & 1) != 0;
	EPDIssued  = (OperatingStatus & 4) != 0;
	EPDLocked = (OperatingStatus & 0x10)!=0;
	EPDTesting = (OperatingStatus & 0x20) != 0;
	EPDProcSec = (OperatingStatus & 0x40) != 0;
	EPDFaulty = (OperatingStatus & 0x80) != 0;

	EPDNewFault = (ErrorStatus & 1) != 0;
	EPDComFault = (ErrorStatus & 2) != 0;
	EPDCalFault = (ErrorStatus & 4) != 0;
	EPDDetFault = (ErrorStatus & 8) != 0;
	EPDEprFault = (ErrorStatus & 0x10) != 0;
	EPDTrhFault = (ErrorStatus & 0x20) != 0;
	EPDOthFault = (ErrorStatus & 0x80) != 0;

	AlarmDoseHP101G = (AlarmStatus & 1) != 0;
	AlarmDoseHP102G = (AlarmStatus & 2) != 0;
	AlarmDoseHP07N = (AlarmStatus & 4) != 0;
	AlarmRateHP101G = (AlarmStatus & 8) != 0;;
	AlarmRateHP102G = (AlarmStatus & 0x10) != 0;
	AlarmRateHP07N = (AlarmStatus & 0x20) != 0;

	//OtherAlarm
	OtherAlarmRateHp101G = (OtherAlarms & 1) != 0;
	OtherAlarmRateHp102G = (OtherAlarms & 2) != 0;
	OtherAlarmRateHp07N = (OtherAlarms & 4) != 0;
	OtherAlarmReturn = (OtherAlarms & 8) != 0;
	OtherAlarmBatLow =  (OtherAlarms & 0x10) != 0;
	OtherAlarmCarrier = (OtherAlarms & 0x20) != 0;
	OtherAlarmBatVolt = (OtherAlarms & 0x40) != 0;

	if (!VersionOK) OtherAlarms &= 0x7f;
	hasAlarm = (OtherAlarms != 0) || (AlarmStatus != 0);
}

/// <summary>
/// 
/// </summary>
/// <returns>0 = ok</returns>
WORD EpdBase::Epd2U::ReadStatus()
{
	int  rv = 0;
	Error = 0; ReadStatusRV = 0;
	memset(WearerID, 0, WearerIDLength + 1);
	memset(WearerName, 0, WearerNameLength + 1);

	IssueCount = OperatingStatus = ErrorStatus = AlarmStatus = OtherAlarms = EEPROMBadSectors = 0;
	RealTime = EpdClock = OffTime = EpdID = 0;
	ErrorSource = ErrorReason = ErrorData = 0;
	HardVersion = SoftVersion = FunctionFlags = 0;

	if (port == 0) {
		EpdID = 4567;
		EpdType = 0;
		FunctionFlags = 0x000;
		return 0;
	}

	RealTime = UnixTimeNow();
	CommitRV = ReadEpdStatus(&EpdClock, &RealTime, &IssueCount, &OffTime, &OperatingStatus, &ErrorStatus, &AlarmStatus, &OtherAlarms, &EEPROMBadSectors);
	if (port == 0) {
		EpdClock = 123; ErrorStatus = 0;
	};
	if (CommitRV == 1) CommitRV = Commit();

	if (CommitRV == 1) CommitRV = ReadEpdID(
		&EpdID,
		&HardVersion,
		&SoftVersion,
		&FunctionFlags);
	if (CommitRV == 1) CommitRV = Commit();
	if (CommitRV == 0) {
		Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);
		ReadStatusRV = ErrorReason;
		rv = ErrorReason;
		return rv;
	}
	else {
		CurrentDeviceID = EpdID;
		time_t t = RealTime;
		TimeFunctions::UnixTimeToSystemTime(t, &tRealTime);
		DecodeStatus();
		DecodeFunctionFlags();
	}

	byte ff = (FunctionFlags >> 8) & 0x0f;
	isBG = ff == 0;
	isNG = ff == 3;
	VersionOK = (isBG && SoftVersion >= 11) || (isNG && SoftVersion >= 3);

	if (CommitRV == 1 && EPDIssued == 1)
	{
		CommitRV = ReadWearer(
			WearerID,
			WearerIDLength,
			WearerName,
			WearerNameLength);
		if (CommitRV == 1) CommitRV = Commit();
		if (CommitRV == 0) {
			Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);
			rv = ReadStatusRV = ErrorReason;
			return rv;
		}
		else {
			rv = 0;
			for (int i = 0; i < WearerIDLength; i++)
				if (WearerID[i] == 0xb) WearerID[i] = '0'; else  WearerID[i] |= 0x30;
		}
	}
	return rv;
}
/// <summary>
/// 
/// </summary>
/// <returns>0 ok</returns>
WORD EpdBase::Epd2U::EPDReadExtraStatus()
{
	int rv = 0;
	if (port == 0) return 1;

	byBatADC = bySupplyADC = 0;
	/*CommitRV = ReadA2ConfigStatus(
		&byBatADC,
		&bySupplyADC,
		&byStatus3,
		&byStatus4);
	if (CommitRV == 1) CommitRV = Commit();*/
	if (CommitRV == 1) CommitRV = ReadCounts(&D1, &D2, &D3, &D4, &CountsClock);
	if (CommitRV == 1) CommitRV = Commit();
	if (CommitRV == 1) CommitRV = ReadBaseCounts(&BaseD1, &BaseD2, &BaseD3, &BaseD4, &BaseCountsClock);
	if (CommitRV == 1) CommitRV = Commit();
	if (CommitRV == 1) rv = 0;
	else {
		Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);
		rv = ErrorReason;
	}
	return rv;
}
WORD EpdBase::Epd2U::EPDReadConfig()
{
	if (port == 0) return 1;

	ReadConfigRV = ReadConfig(
		&CountDownTime,
		&ReturnTime,
		&ChirpRate,
		&TeleBaudSetting,
		&SelfTestInterval,
		&BatteryLoadTestInt,
		&GeneralControl,
		&CommsTimeout,
		&InhibitTimeout,
		&MinTxInterval,
		&MaxTxIntervals,
		&RandomiseWindow,
		&DoseIncThresh);
	if (ReadConfigRV == 0) Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);
	CommitRV = Commit();
	if (CommitRV == 0) Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);
	return CommitRV;
}


/// <summary>
/// 
/// </summary>
/// <returns>0=ok</returns>
WORD EpdBase::Epd2U::WriteCal()
{
	int rv = 1;
	CommitRV = 0;
	if (port == 0 ) return 0;
	if (isBG) return 1;
	if (K6 > 2000) return 1;
	Password = 9;
	//((BYTE *)&Password)[0] = 9;
	
	CommitRV = MFRLogin(Password);
	if (CommitRV == 1) CommitRV = Commit();
	if (CommitRV == 1) CommitRV = MFRWriteCalFactors(K1, K2, K3, K4, K5, K6);
	if (CommitRV == 1) CommitRV = Commit();
	if (CommitRV == 0) Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);
	else rv = 0; //ok
	return rv;
}


WORD EpdBase::Epd2U::ReadCal()
{
	ReadCalibrationDataRV = ReadCalibrationData(
		&K1,
		&K2,
		&K3,
		&K4,
		&K5,
		&K6,
		&SG,
		&BC,
		&FB,
		&HG);
	CommitRV = Commit();
	if (CommitRV == 0) Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);
	if (CommitRV == 1) return 0; else return 123;
}
/// <summary>
/// 
/// </summary>
/// <returns>0 Success</returns>
WORD EpdBase::Epd2U::ReadTresHolds()
{
	int rv =0;
	if (port == 0) {
		Hp10THDose1 = Hp10THDose2 = Hp10THDose2 = 123;
		Hp10Rate1On = Hp10Rate2On = Hp07RateOn = Hp10Rate1Off = Hp10Rate2Off = Hp07RateOff = 124;
		return 1;
	}
	Hp10THDose1 = Hp10THDose2 = Hp07THDose = 0;
	ReadDoseTresholdRV = ReadAlarmDoseThresholds(&Hp10THDose1, &Hp10THDose2, &Hp07THDose);
	CommitRV = Commit();
	if (CommitRV == 0) rv = Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);

	if (rv == 0) {
		ReadAlarmRateThresholdsRV = ReadAlarmRateThresholds(
			&Hp10Rate1On,
			&Hp10Rate2On,
			&Hp07RateOn,
			&Hp10Rate1Off,
			&Hp10Rate2Off,
			&Hp07RateOff);
		CommitRV = Commit();
		if (CommitRV == 0) rv = Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);
	}
	if (rv==0)	rv = ReadCal();
	return rv;
}

/// <summary>
/// 
/// </summary>
/// <returns>0 OK</returns>
WORD EpdBase::Epd2U::ReadDoses()
{
	if (port == 0) {
		Hp10Dose = Hp07Dose = Hp10Total = Hp07Total = Knocks = Resets = QualityData = 21;
		Hp10Peak = Hp07Peak = Hp10Time = Hp07Time = 1000;
		ErrorSource = ErrorReason = ErrorData = 0;
		K1 = 1; K2 = 2; K3 = 3; K4 = 4; K5 = 5;  K6 = 6;
		SG = 7; BC = 8; FB = 9; HG = 10;
		return 1;
	}
	ReadDosesQualityRV = ReadPeaksRV = CommitRV = 0;
	ReadDosesQualityRV = ReadDosesQuality(
			&Hp10Dose, 
			&Hp07Dose,
			&Hp10Total, 
			&Hp07Total,
			&Knocks, 
			&Resets,
			&QualityData);

	if (ReadDosesQualityRV == 0) {
		Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);
		return 0;
	}

	ReadPeaksRV = ReadPeaks(
		&Hp10Peak,
		&Hp07Peak,
		&Hp10Time,
		&Hp07Time);
	if (ReadPeaksRV == 0) {
		Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);
		return 0;
	}

	TimeFunctions::EPD2PeakTimeToFiletime(EpdClock, Hp10Time, (LPFILETIME) &Hp10FTime);
	TimeFunctions::EPD2PeakTimeToFiletime(EpdClock, Hp07Time, (LPFILETIME) &Hp07FTime);

	CommitRV = Commit();
	if (CommitRV == 0) Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);

	ReadCalibrationData(
		&K1,
		&K2,
		&K3,
		&K4,
		&K5,
		&K6,
		&SG,
		&BC,
		&FB,
		&HG);
	CommitRV = Commit();
	if (CommitRV == 0) Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);
	return CommitRV==1 ? 0 : 1;
}
WORD EpdBase::Epd2U::PrepareForIssue() 
{
	int rv = 0;
	if (port == 0) return 0;
	CommitRV = WriteConfig();
	if (CommitRV) CommitRV = Commit();
	if (CommitRV) CommitRV = ClearDose();
	if (CommitRV) CommitRV = ClearTotalDose();
	if (CommitRV) CommitRV = ClearPeakRates();
	if (CommitRV) CommitRV = Commit();
	if (CommitRV) rv = 0; else rv = 1;
	return rv;
}
/// <summary>
/// DeIssue and close
/// </summary>
/// <returns></returns>
WORD EpdBase::Epd2U::DeIssue()
{
	int rv = 0;
	ClearDoseRV = ClearTotalDoseRV = ClearPeakRatesRV = DeIssueEpdRV = 0;
	if (port == 0) return 1;
	if (CommitRV) CommitRV = ClearDose();
	if (CommitRV) CommitRV = ClearTotalDose();
	if (CommitRV) CommitRV = ClearPeakRates();
	if (CommitRV) CommitRV = Commit();
	if (CommitRV) CommitRV = ClearFaultFlags();
	if (CommitRV) CommitRV = ClearLatchedAlarms();//Hp10Rate1; Hp10Rate2; Hp07Rate; ReturnForRead
	if (CommitRV) CommitRV = Commit();
	CommitRV = ClearFailFlag();
	if (CommitRV && EPDIssued) {
		CommitRV = DeIssueEpd();
		if (CommitRV) CommitRV = Commit();
	}
	if (CommitRV && DetectorsOn ) {
		CommitRV = EpdOff();  
		CommitRV= Commit(); 
		if (!VersionOK) Sleep(1000);// Versie < .. Commit & Sleep
	}
	
	if (CommitRV ) {
		CloseAndSignal(EndSessionControl, 12); 
		rv = 0;
	}
	else
	{
		FlushBuffers(port);
		Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData); 
		CloseAndSignal(EndSessionControl, 40);
		rv = ErrorReason;
	}
	return rv;
}
/// <summary>
/// 
/// </summary>
/// <returns>O = OK</returns>
WORD EpdBase::Epd2U::Issue()
{
	int rv=0;
	IssueEpdRV = WriteAlarmDoseThresholdsRV = WriteAlarmRateThresholdsRV = WriteWearerNameRV = OnRV = CommitRV = 0;
	if (port == 0) return 0;
	WORD OKRV = SetFailFlag(); // all actions have been completed
	if (OKRV) OKRV = Commit();
	if (OKRV) OKRV = BaseLineCounts();
	if (OKRV) OKRV = WriteAlarmDoseThresholds(Hp10THDose1, Hp10THDose2, Hp07THDose);
	if (OKRV) OKRV = WriteAlarmRateThresholds(Hp10Rate1On, Hp10Rate2On, Hp07RateOn, Hp10Rate1Off, Hp10Rate2Off, Hp07RateOff);
	if (OKRV) OKRV = Commit();
	if (OKRV) OKRV = IssueEpd(WearerID, WearerIDLength);
	if (OKRV) OKRV = Commit();
	if (OKRV) OKRV = WriteWearerName(WearerName,(WORD)strlen((char *)WearerName));
	if (OKRV) OKRV = Commit();
	
	if (OKRV == 0)
	{
		Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData);
		rv = 1;
	}
	ClearFailFlag(); // all actions have been completed

	
	if (OKRV) 
	{
		if (!DetectorsOn) {
			OKRV = EpdOn();
			OKRV = Commit();
			if (!VersionOK) Sleep(1500);
		}
		CloseAndSignal(EndSessionControl, 12);
		rv = 0;
	}
	else 
	{ 
		FlushBuffers(port);
		Error = GetErrorDetails(&ErrorSource, &ErrorReason, &ErrorData); 
		CloseAndSignal(EndSessionControl, 40);
		rv = 1;
	}
	return rv;
}


int EpdBase::Epd2U::GetAllDeissue()
{
	int rv = 0;
	rv = ReadDoses();	  //0 = success
	//rv = ReadTresHolds(); //tresholds not needed, only doses
	rv = EPDReadExtraStatus();
	rv = DeIssue(); //does close
	return rv;
}


int EpdBase::Epd2U::GetAll(bool Closeconnection)
{
	int rv = 0;
	rv = ReadDoses();	  //0 = success
	rv = ReadTresHolds(); //0 = success Tresholds needed
	if (Closeconnection) Close();
	return rv;
}

/// <summary>
/// discover, connect and read status
/// </summary>
/// <param name="tokenSourceTimeout"></param>
/// <returns>0=success 1=connect error,  2 = no devices, 3= error reading status</returns>
int EpdBase::Epd2U::ConnectAndStatus(int tokenSourceTimeout)
{
	WORD dllStatus;
	int rv = 1;
	if (CommsInitialized == 0) StartCom();
	dllStatus = StartDiscover(tokenSourceTimeout);
	if (dllStatus != 1) rv = 1; //Connect error
	else if (DeviceCount < 1) rv = 2;  //TO
	else if (ReadStatus() != 1) rv = 1; //Read Error
	else rv = 0;
	return rv;
}

int EpdBase::Epd2U::IssueAndCheck(float gain)
{
	int rv = 0;
	if (isNG) {
		int kx = (int)gain;
		rv = ReadCal();
		if (rv != 0 || K6 != 1310) { rv = 1; goto error; }
		if (K3 != kx || K4 != kx)
		{
			K3 = K4 = kx;
			rv = WriteCal();
			if (rv != 0) goto error;
		}
	}
	rv = PrepareForIssue();
	rv = Issue(); // data already loaded
	return rv;
error:
	return rv;
}