unit DACExports;

interface

uses
	Winapi.Windows,
	System.SysUtils,
	Core,
	CoreDataMessages, ENetDataItems;

function DAC_GetRange(uClientRef: TClientRef; Ch: TChannelIndex; pRange: TPRangeCode = nil; pLowV: TPDouble = nil; pHighV: TPDouble = nil): TWinErrCode; cdecl;

function DAC_Enable(uClientRef: TClientRef): TWinErrCode; cdecl;
function DAC_Disable(uClientRef: TClientRef): TWinErrCode; cdecl;

function DAC_OutputCounts(uClientRef: TClientRef; Ch: TChannelIndex {= ciAll}; Counts: TUInt16): TWinErrCode; cdecl;
function DAC_OutputFrac(uClientRef: TClientRef; Ch: TChannelIndex {= ciAll}; Frac: TDouble): TWinErrCode; cdecl;
function DAC_OutputV(uClientRef: TClientRef; Ch: TChannelIndex {= ciAll}; V: TDouble): TWinErrCode; cdecl;
function DAC_OutputNCounts(uClientRef: TClientRef; Channels: TUInt32; pCounts: TPUInt16): TWinErrCode; cdecl;
function DAC_OutputNFrac(uClientRef: TClientRef; Channels: TUInt32; pFrac: TPDouble): TWinErrCode; cdecl;
function DAC_OutputNV(uClientRef: TClientRef; Channels: TUInt32; pV: TPDouble): TWinErrCode; cdecl;

function DAC_SetChCal(uClientRef: TClientRef; Ch: TChannelIndex; ScaleFactor: TDouble = 1; OffsetCounts: TDouble = 0): TWinErrCode; cdecl;
function DAC_SetAllCal(uClientRef: TClientRef; pScaleFactor: TPDouble = nil; pOffsetCounts: TPDouble = nil): TWinErrCode; cdecl;

function DAC_RecordRange(uClientRef: TClientRef; Ch: TChannelIndex {= ciAll}; RangeCode: TRangeCode): TWinErrCode; cdecl;

//Not a good idea as a separate function, given such delays over many/most networks.
//function DAC_Configure(uClientRef: TClientRef; RangeCt: TUInt32; pRange: TPRangeCode): TWinErrCode; cdecl;





implementation

const
{
	LTC2664Cmd_Data      = $3; //Write code to N, update N.
	LTC2664Cmd_Range     = $6; //Write span to N.
	LTC2664Cmd_Data_All  = $A; //Write code to all, update all, power up.
	LTC2664Cmd_Range_All = $E; //Write span to all.
}
	AD5627Cmd_Data      = $3; //Write code to N, update N.
	AD5627Cmd_Data_Load = $3; //Write code to N, don't update.
	AD5627Cmd_Data_LDAC = $3; //Write code to N, update all.
	AD5627Cmd_Ref       = $7; //Enable/disable reference.

{
function LTC2664_WriteDAC(ToClient: TRefClient; Command, Address: Byte; Data: TUInt16): TWinErrCode;
var
	PacketBuf: AnsiString;
	Control: TUInt32;

begin
	Control := Data or ((Address and $F) shl 16) or ((Command and $F) shl 20);
	PacketBuf := AnsiString(IntToHex(Control, 8));

	PacketBuf := 'CFJ04' + PacketBuf;

	Result := ToClient.SendReceivePacket(PacketBuf, PacketBuf);
	if Result <> ERROR_SUCCESS then Exit;

	//Check reply.
	if PacketBuf <> 'A' then begin
		Result := ERROR_BAD_NET_RESP;
		Exit;
	end;
end;
}

function AD5627_WriteDAC_DataItem(Command, Address: Byte; Data: TUInt16): TWriteRegDataItem;
begin
	Result := TWriteRegDataItem.Create;
	Result.RegOff := $30;
	Result.RegData32 := Data or ((Address and $F) shl 16) or (Command shl 20);
end;

function AD5627_WriteDAC(ToClient: TRefClient; Command, Address: Byte; Data: TUInt16): TWinErrCode;
var
	Msg: TMessageItem;

begin
	Msg := TMessageItem.Create;

	Msg.AddDataItem(AD5627_WriteDAC_DataItem(Command, Address, Data));

	Result := ToClient.SendReceiveMsgAndExpectOK(Msg);

	Msg.Free;
end;

function AD5627_WriteDAC_Enables(ToClient: TRefClient; Data: TUInt16): TWinErrCode;
var
	Msg: TMessageItem;

begin
	Msg := TMessageItem.Create;

	Msg.AddDataItem(AD5627_WriteDAC_DataItem(AD5627Cmd_Ref, 0, Data));
	Msg.AddDataItem(AD5627_WriteDAC_DataItem(AD5627Cmd_Ref, 2, Data));
	with TWriteRegDataItem(Msg.AddNewDataItem(TWriteRegDataItem)) do begin
		RegOff := $34;
		RegData32 := Data;
	end;

	Result := ToClient.SendReceiveMsgAndExpectOK(Msg);

	Msg.Free;
end;

function AD5627_WriteDAC_All(ToClient: TRefClient; Command: Byte; Data: TUInt16): TWinErrCode;
var
	iCh: TSIntNative;
	Msg: TMessageItem;

begin
	Msg := TMessageItem.Create;

	for iCh := 0 to DACCt-1 do
		with TWriteRegDataItem(Msg.AddNewDataItem(TWriteRegDataItem)) do begin
			RegOff := $30;
			RegData := Data or ((iCh and $F) shl 16) or (Command shl 20);
			RegDataSize := rds32;
		end
	;

	Result := ToClient.SendReceiveMsgAndExpectOK(Msg);

	Msg.Free;
end;

(*
function DAC_Configure(uClientRef: TClientRef; RangeCt: TUInt32; pRange: TPRangeCode): TWinErrCode; cdecl;
var
	ThisClient: TRefClient;
	Ranges: array of TRangeCode;
	iCh: Integer;

begin
	Result := VerifyClientRef(uClientRef);
	if Result <> ERROR_SUCCESS then Exit;

	ThisClient := Pointer(uClientRef);

	try
		if RangeCt = 0 then Exit;

		if pRange = nil then begin
			Result := ERROR_INVALID_ADDRESS;
			Exit;
		end;

		if RangeCt > DACCt then RangeCt := DACCt;
		//Copy in the provided ranges, with room for any extras needed.
		SetLength(Ranges, DACCt);
		try
			Move(pRange^, Ranges[0], RangeCt);
		except
			Result := ERROR_INVALID_ADDRESS;
			Exit;
		end;
		//Validate.
		for iCh := 0 to RangeCt-1 do begin
			Result := ValidateDACRange(Ranges[iCh]);
			if Result = ERROR_HANDLE_EOF then Result := ERROR_SUCCESS;
			if Result <> ERROR_SUCCESS then Exit;
		end;
		//If they didn't provide enough, then continue the last one.
		if RangeCt < DACCt then
			for iCh := RangeCt to DACCt-1 do
				Ranges[iCh] := Ranges[RangeCt-1]
		;
		//Store.
		for iCh := 0 to DACCt-1 do
			ThisClient.LastDACRanges[iCh] := Ranges[iCh]
		;
		//Write.
		for iCh := 0 to DACCt-1 do begin
			if Ranges[iCh] = rcNoChange then Continue;

			Result := LTC2664_WriteDAC(ThisClient, LTC2664Cmd_Range, iCh, Ranges[iCh]);
			if Result <> ERROR_SUCCESS then Exit;
		end;
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;
*)



function DAC_GetRange(uClientRef: TClientRef; Ch: TChannelIndex; pRange: TPRangeCode = nil; pLowV: TPDouble = nil; pHighV: TPDouble = nil): TWinErrCode; cdecl;
var
	ThisClient: TRefClient;
	DACRangeParams: TRangeParams;
	LowV, HighV: Double;

begin
	Result := VerifyClientRef(uClientRef);
	if Result <> ERROR_SUCCESS then Exit;

	ThisClient := Pointer(uClientRef);

	try
		if Ch >= DACCt then begin
			Result := ERROR_INVALID_INDEX;
			Exit;
		end;

		try
			if pRange <> nil then pRange^ := ThisClient.DACRange[Ch];
			if (pLowV <> nil) or (pHighV <> nil) then begin
				Result := CalcRangeParams(ThisClient.DACRange[Ch], DACRangeParams);
				if Result <> ERROR_SUCCESS then Exit;

				if DACRangeParams.bBipolar then begin
					LowV := -1/2 * DACRangeParams.Span;
					HighV := +1/2 * DACRangeParams.Span;
				end
				else begin
					LowV := 0;
					HighV := DACRangeParams.Span;
				end;
				if pLowV <> nil then pLowV^ := LowV;
				if pHighV <> nil then pHighV^ := HighV;
			end;
		except
			Result := ERROR_NOACCESS;
			//Exit;
		end;
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;



function DAC_Enable(uClientRef: TClientRef): TWinErrCode; cdecl;
var
	ThisClient: TRefClient;

begin
	Result := VerifyClientRef(uClientRef);
	if Result <> ERROR_SUCCESS then Exit;

	ThisClient := Pointer(uClientRef);

	try
		Result := AD5627_WriteDAC_Enables(ThisClient, 1);
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;

function DAC_Disable(uClientRef: TClientRef): TWinErrCode; cdecl;
var
	ThisClient: TRefClient;

begin
	Result := VerifyClientRef(uClientRef);
	if Result <> ERROR_SUCCESS then Exit;

	ThisClient := Pointer(uClientRef);

	try
		Result := AD5627_WriteDAC_Enables(ThisClient, 0);
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;



function CountsFromFrac(F: TDouble): TUInt16;
const
	MaxFrac = $FFFF/$10000;

begin
	if F < 0 then
		F := 0
	else if F > MaxFrac then
		F := MaxFrac
	;
	Result := Round($10000 * F);
end;

function DAC_OutputCounts(uClientRef: TClientRef; Ch: TChannelIndex {= ciAll}; Counts: TUInt16): TWinErrCode; cdecl;
var
	ThisClient: TRefClient;

begin
	Result := VerifyClientRef(uClientRef);
	if Result <> ERROR_SUCCESS then Exit;

	ThisClient := Pointer(uClientRef);

	try
		if (Ch <> ciAll) and (Ch >= DACCt) then begin
			Result := ERROR_INVALID_INDEX;
			Exit;
		end;

		if Ch = ciAll then
			Result := AD5627_WriteDAC_All(ThisClient, AD5627Cmd_Data, Counts)
		else
			Result := AD5627_WriteDAC(ThisClient, AD5627Cmd_Data, Ch, Counts)
		;
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;

function DAC_OutputFrac(uClientRef: TClientRef; Ch: TChannelIndex {= ciAll}; Frac: TDouble): TWinErrCode; cdecl;
begin
	try
		Result := DAC_OutputCounts(uClientRef, Ch, CountsFromFrac(Frac));
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;

function DAC_OutputV(uClientRef: TClientRef; Ch: TChannelIndex {= ciAll}; V: TDouble): TWinErrCode; cdecl;
var
	ThisClient: TRefClient;
	nCh, xCh: TChannelIndex;
	Data: array of TUInt16;
	Msg: TMessageItem;

begin
	Result := VerifyClientRef(uClientRef);
	if Result <> ERROR_SUCCESS then Exit;

	ThisClient := Pointer(uClientRef);

	try
		SetLength(Data, DACCt);
		if Ch = ciAll then begin
			nCh := 0;
			xCh := DACCt-1;
		end
		else if Ch >= DACCt then begin
			Result := ERROR_INVALID_INDEX;
			Exit;
		end
		else begin
			nCh := Ch;
			xCh := Ch;
		end;

		for Ch := nCh to xCh do begin
			Result := ThisClient.VToCounts(Ch, V, Data[Ch]);
			if Result <> ERROR_SUCCESS then Exit;
		end;

		Msg := TMessageItem.Create;
		try
			for Ch := nCh to xCh do begin
				Msg.AddDataItem(AD5627_WriteDAC_DataItem(AD5627Cmd_Data, Ch, Data[Ch]));
			end;

			Result := ThisClient.SendReceiveMsgAndExpectOK(Msg);
		finally
			Msg.Free;
		end;
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;

function DAC_OutputNCounts_Inner(ThisClient: TRefClient; const Counts: array of TUInt16): TWinErrCode;
var
	Msg: TMessageItem;
	iCh: TChannelIndex;

begin
		Msg := TMessageItem.Create;
		try
			for iCh := 0 to High(Counts) do begin
				Msg.AddDataItem(AD5627_WriteDAC_DataItem(AD5627Cmd_Data, iCh, Counts[iCh]));
			end;

			Result := ThisClient.SendReceiveMsgAndExpectOK(Msg);
		finally
			Msg.Free;
		end;
end;

function DAC_OutputNCounts(uClientRef: TClientRef; Channels: TUInt32; pCounts: TPUInt16): TWinErrCode; cdecl;
var
	ThisClient: TRefClient;
	Counts: array of TUInt16;

begin
	Result := VerifyClientRef(uClientRef);
	if Result <> ERROR_SUCCESS then Exit;

	ThisClient := Pointer(uClientRef);

	try
		if Channels > DACCt then Channels := DACCt;

		SetLength(Counts, Channels);
		try
			Move(pCounts^, Counts[0], SizeOf(pCounts^) * Channels);
		except
			Result := ERROR_NOACCESS;
			Exit;
		end;

		Result := DAC_OutputNCounts_Inner(ThisClient, Counts);

		SetLength(Counts, 0);
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;

function DAC_OutputNFrac(uClientRef: TClientRef; Channels: TUInt32; pFrac: TPDouble): TWinErrCode; cdecl;
var
	ThisClient: TRefClient;
	ThisFrac: array of TDouble;
	Counts: array of TUInt16;
	iCh: Integer;

begin
	Result := VerifyClientRef(uClientRef);
	if Result <> ERROR_SUCCESS then Exit;

	ThisClient := Pointer(uClientRef);

	try
		if Channels > DACCt then Channels := DACCt;

		SetLength(ThisFrac, Channels);
		try
			Move(pFrac^, ThisFrac[0], SizeOf(pFrac^) * Channels);
		except
			Result := ERROR_NOACCESS;
			Exit;
		end;

		SetLength(Counts, Channels);
		for iCh := 0 to High(Counts) do
			Counts[iCh] := CountsFromFrac(ThisFrac[iCh])
		;
		SetLength(ThisFrac, 0);

		Result := DAC_OutputNCounts_Inner(ThisClient, Counts);

		SetLength(Counts, 0);
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;

function DAC_OutputNV(uClientRef: TClientRef; Channels: TUInt32; pV: TPDouble): TWinErrCode; cdecl;
var
	ThisClient: TRefClient;
	ThisV: array of TDouble;
	Counts: array of TUInt16;
	iCh: Integer;

begin
	Result := VerifyClientRef(uClientRef);
	if Result <> ERROR_SUCCESS then Exit;

	ThisClient := Pointer(uClientRef);

	try
		if Channels > DACCt then Channels := DACCt;

		SetLength(ThisV, Channels);
		try
			Move(pV^, ThisV[0], SizeOf(pV^) * Channels);
		except
			Result := ERROR_NOACCESS;
			Exit;
		end;

		SetLength(Counts, Channels);
		for iCh := 0 to High(Counts) do begin
			Result := ThisClient.VToCounts(iCh, ThisV[iCh], Counts[iCh]);
			if Result <> ERROR_SUCCESS then Exit;
		end;
		SetLength(ThisV, 0);

		Result := DAC_OutputNCounts_Inner(ThisClient, Counts);

		SetLength(Counts, 0);
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;



function DAC_SetChCal(uClientRef: TClientRef; Ch: TChannelIndex; ScaleFactor: TDouble = 1; OffsetCounts: TDouble = 0): TWinErrCode; cdecl;
var
	ThisClient: TRefClient;
	iCh, nCh, xCh: TChannelIndex;
	Msg: TMessageItem;

begin
	Result := VerifyClientRef(uClientRef);
	if Result <> ERROR_SUCCESS then Exit;

	ThisClient := Pointer(uClientRef);

	try
		if Ch = ciAll then begin
			nCh := 0;
			xCh := DACCt-1;
		end
		else if Ch >= DACCt then begin
			Result := ERROR_INVALID_INDEX;
			Exit;
		end
		else begin
			nCh := Ch;
			xCh := Ch;
		end;

		if (ScaleFactor <= 0)
		or (ScaleFactor >= 2)
		or (OffsetCounts <= -$10000)
		or (OffsetCounts >= $10000)
		then begin
			Result := ERROR_INVALID_PARAMETER;
			Exit;
		end;

		Msg := TMessageItem.Create;
		try
			for iCh := nCh to xCh do begin
				with TCalScale1DataItem(Msg.AddNewDataItem(TCalScale1DataItem)) do begin
					Ch := iCh;
					Scale := ScaleFactor;
				end;
				with TCalOffset1DataItem(Msg.AddNewDataItem(TCalOffset1DataItem)) do begin
					Ch := iCh;
					Offset := OffsetCounts;
				end;
			end;

			Result := ThisClient.SendReceiveMsgAndExpectOK(Msg);
		finally
			Msg.Free;
		end;



	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;

function DAC_SetAllCal(uClientRef: TClientRef; pScaleFactor: TPDouble = nil; pOffsetCounts: TPDouble = nil): TWinErrCode; cdecl;
var
	ThisClient: TRefClient;
	iCh: TChannelIndex;
	Msg: TMessageItem;
	ScaleFactor: array[0..3] of TSingle;
	OffsetCounts: array[0..3] of TSingle;

begin
	Result := VerifyClientRef(uClientRef);
	if Result <> ERROR_SUCCESS then Exit;

	ThisClient := Pointer(uClientRef);

	try
		try
			if pScaleFactor <> nil then
				for iCh := 0 to DACCt-1 do begin
					ScaleFactor[iCh] := pScaleFactor^;
					Inc(pScaleFactor);
				end
			;
			if pOffsetCounts <> nil then
				for iCh := 0 to DACCt-1 do begin
					OffsetCounts[iCh] := pOffsetCounts^;
					Inc(pOffsetCounts);
				end
			;
		except
			Result := ERROR_NOACCESS;
			Exit;
		end;

		if pScaleFactor <> nil then
			for iCh := 0 to DACCt-1 do
				if (ScaleFactor[iCh] <= 0)
				or (ScaleFactor[iCh] >= 2)
				then begin
					Result := ERROR_INVALID_PARAMETER;
					Exit;
				end
		;
		if pOffsetCounts <> nil then
			for iCh := 0 to DACCt-1 do
				if (OffsetCounts[iCh] <= -$10000)
				or (OffsetCounts[iCh] >= $10000)
				then begin
					Result := ERROR_INVALID_PARAMETER;
					Exit;
				end
		;

		Msg := TMessageItem.Create;
		try
			if pScaleFactor <> nil then
				with TCalScaleAllDataItem(Msg.AddNewDataItem(TCalScaleAllDataItem)) do begin
					Count := DACCt;
					for iCh := 0 to DACCt-1 do
						Scale[iCh] := ScaleFactor[iCh]
				end
			;
			if pOffsetCounts <> nil then
				with TCalOffsetAllDataItem(Msg.AddNewDataItem(TCalOffsetAllDataItem)) do begin
					Count := DACCt;
					for iCh := 0 to DACCt-1 do
						Offset[iCh] := OffsetCounts[iCh]
				end
			;

			Result := ThisClient.SendReceiveMsgAndExpectOK(Msg);
		finally
			Msg.Free;
		end;



	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;



function DAC_RecordRange(uClientRef: TClientRef; Ch: TChannelIndex {= ciAll}; RangeCode: TRangeCode): TWinErrCode; cdecl;
var
	ThisClient: TRefClient;
	Msg: TMessageItem;
	DI: TDACRange1DataItem;
	iCh: Integer;

begin
	Result := VerifyClientRef(uClientRef);
	if Result <> ERROR_SUCCESS then Exit;

	ThisClient := Pointer(uClientRef);

	try
		if (Ch <> ciAll) and (Ch >= DACCt) then begin
			Result := ERROR_INVALID_INDEX;
			Exit;
		end;

		case RangeCode of
			rcUnipolar10, rcUnipolar5, rcUnipolar2, rcUnipolar1, rcBipolar24, rcBipolar20, rcBipolar12, rcBipolar10, rcBipolar5, rcBipolar2_5, rcBipolar2, rcBipolar1_25, rcBipolar1, rcBipolar_625, rcBipolar_3125: ;
			else begin
				Result := ERROR_INVALID_DATA;
				Exit;
			end;
		end;

		Msg := TMessageItem.Create;
		if Ch < DACCt then begin
			DI := TDACRange1DataItem.Create;
			DI.Ch := Ch;
			DI.RangeCode := RangeCode;
			Msg.AddDataItem(DI);
		end
		else for iCh := 0 to DACCt-1 do begin
			DI := TDACRange1DataItem.Create;
			DI.Ch := iCh;
			DI.RangeCode := RangeCode;
			Msg.AddDataItem(DI);
		end;

		Result := ThisClient.SendReceiveMsgAndExpectOK(Msg);

		Msg.Free;

		if Result = ERROR_SUCCESS then begin
			if Ch < DACCt then begin
				ThisClient.DACRange[Ch] := RangeCode;
			end
			else for iCh := 0 to DACCt-1 do begin
				ThisClient.DACRange[iCh] := RangeCode;
			end;
		end;
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;

end.