unit ENetDataItems;

interface

uses
	SysUtils,
	CoreDataMessages;

const
	//MIDConfig = TMID('C');
	//MIDQuery = TMID('Q');
	MIDMixed = TMID('M');
	MIDReply = TMID('R');
	MIDError = TMID('E');
	MIDHello = TMID('H');

const
	DIDHelloConnectionID = $7001;
	DIDHelloDeviceID = $0002;
	DIDHelloFeatures = $0003;
	DIDHelloFPGAVer = $0004;
	DIDHelloBaseClock = $1003;
	DIDReadReg = $0101;
	DIDWriteReg = $0105;
	DIDADCStreamStart = $1101;
	DIDADCStreamStop = $1102;
	DIDDACRange = $0204;
	DIDHostname = $9001;
	DIDDACCalibrate1 = $20C;
	DIDDACCalibrateAll = $20D;
	DIDDACScale1 = $210;
	DIDDACScaleAll = $211;
	DIDDACOffset1 = $20E;
	DIDDACOffsetAll = $20F;

type
	TRegDataSizeOrNone = (rdsNone, rds8, {rds16,} rds32{, rds64});
	TRegDataSizeOrNones = set of TRegDataSizeOrNone;

	TRegDataSize = rds8..High(TRegDataSizeOrNone);
	TRegDataSizes = set of TRegDataSize;

	TRegDataItem = class(TDataItem)
	public
		RegOff: Byte;
	end;

	T2Or5RegDataItem = class(TRegDataItem)
	public
		RegData: LongWord;
		RegDataSize: TRegDataSize;
	protected
		procedure SetRegData8(NewRegData: Byte); virtual;
		procedure SetRegData32(NewRegData: LongWord); virtual;
	public
		property RegData8: Byte write SetRegData8;
		property RegData32: LongWord write SetRegData32;
	protected
		function CalcPayload: AnsiString; override;
		procedure SetPayload(NewPayload: AnsiString); override;
	end;

	TReadRegDataItem = class(TRegDataItem)
	public
		constructor Create; override;
		class function OKReplyType: TDataItemClass; override;
	protected
		function CalcPayload: AnsiString; override;
		procedure SetPayload(NewPayload: AnsiString); override;
	end;

	TReadRegReplyDataItem = class(T2Or5RegDataItem)
	public
		constructor Create; override;
	end;

	TWriteRegDataItem = class(T2Or5RegDataItem)
	public
		constructor Create; override;
		class function OKReplyType: TDataItemClass; override;
	end;

	TWriteRegReplyDataItem = class(T2Or5RegDataItem)
	public
		constructor Create; override;
	end;

	TADCStreamStartDataItem = class(TDataItem)
	public
		ConnectionID: LongWord;
		constructor Create; override;
	protected
		function CalcPayload: AnsiString; override;
		procedure SetPayload(NewPayload: AnsiString); override;
	end;

	TADCStreamStopDataItem = class(TDataItem)
	public
		constructor Create; override;
	end;

	TConnectionIDDataItem = class(TDataItem)
	public
		ConnectionID: LongWord;
		constructor Create; override;
	protected
		function CalcPayload: AnsiString; override;
		procedure SetPayload(NewPayload: AnsiString); override;
	end;

	TDACRange1DataItem = class(TDataItem)
	public
		Ch: Byte;
		RangeCode: LongWord;
		constructor Create; override;
	protected
		function CalcPayload: AnsiString; override;
		procedure SetPayload(NewPayload: AnsiString); override;
	end;

	TDeviceIDDataItem = class(TDataItem)
	public
		DeviceID: Word;
		constructor Create; override;
	protected
		function CalcPayload: AnsiString; override;
		procedure SetPayload(NewPayload: AnsiString); override;
	end;

	TFeaturesDataItem = class(TDataItem)
	public
		Features: LongWord;
		constructor Create; override;
	protected
		function CalcPayload: AnsiString; override;
		procedure SetPayload(NewPayload: AnsiString); override;
	end;

	TFPGAVerDataItem = class(TDataItem)
	public
		Ver: LongWord;
		constructor Create; override;
	protected
		function CalcPayload: AnsiString; override;
		procedure SetPayload(NewPayload: AnsiString); override;
	end;

	TBaseClockDataItem = class(TDataItem)
	public
		BaseClock: LongWord;
		constructor Create; override;
	protected
		function CalcPayload: AnsiString; override;
		procedure SetPayload(NewPayload: AnsiString); override;
	end;

	THostnameDataItem = class(TDataItem)
	public
		Hostname: AnsiString;
		constructor Create; override;
	protected
		function CalcPayload: AnsiString; override;
		procedure SetPayload(NewPayload: AnsiString); override;
	end;

	TCalFactor1DataItem = class(TDataItem)
	public
		Ch: Byte;
	protected
		Factor: Single;

		function CalcPayload: AnsiString; override;
		procedure SetPayload(NewPayload: AnsiString); override;
	end;

	TCalScale1DataItem = class(TCalFactor1DataItem)
	public
		constructor Create; override;
		property Scale: Single read Factor write Factor;
	end;

	TCalOffset1DataItem = class(TCalFactor1DataItem)
	public
		constructor Create; override;
		property Offset: Single read Factor write Factor;
	end;

	TCalFactorAllDataItem = class(TDataItem)
	public
		Ch: Byte;
	protected
		function CalcPayload: AnsiString; override;
		procedure SetPayload(NewPayload: AnsiString); override;
	protected
		Factor: array of Single;
		function GetFactor(I: Integer): Single; virtual;
		procedure SetFactor(I: Integer; NewFactor: Single); virtual;
	protected
		function GetCount: Integer; virtual;
		procedure SetCount(NewCount: Integer); virtual;
	public
		property Count: Integer read GetCount write SetCount;
	end;

	TCalScaleAllDataItem = class(TCalFactorAllDataItem)
	public
		constructor Create; override;
		property Scale[I: Integer]: Single read GetFactor write SetFactor;
	end;

	TCalOffsetAllDataItem = class(TCalFactorAllDataItem)
	public
		constructor Create; override;
		property Offset[I: Integer]: Single read GetFactor write SetFactor;
	end;



implementation

{ T2Or5DataItem }

function T2Or5RegDataItem.CalcPayload: AnsiString;
begin
	case RegDataSize of
		rds8: begin
			SetString(Result, nil, 2);
			PByte(@Result[2])^ := RegData;
		end;
		rds32: begin
			SetString(Result, nil, 5);
			PLongWord(@Result[2])^ := RegData;
		end;
		else begin
			raise Exception.Create('Attempted to calc ' + ClassName + ' payload with invalid size.');
		end;
	end;
	Result[1] := AnsiChar(RegOff);
end;

procedure T2Or5RegDataItem.SetPayload(NewPayload: AnsiString);
begin
	MinPayloadExcept(Length(NewPayload), 2);
	if Length(NewPayload) < 5 then begin
		RegData := Byte(NewPayload[2]);
		RegDataSize := rds8;
	end
	else begin
		RegData := PLongWord(@NewPayload[2])^;
		RegDataSize := rds32;
	end;
	RegOff := Byte(NewPayload[1]);
end;

procedure T2Or5RegDataItem.SetRegData32(NewRegData: LongWord);
begin
	RegData := NewRegData;
	RegDataSize := rds32;
end;

procedure T2Or5RegDataItem.SetRegData8(NewRegData: Byte);
begin
	RegData := NewRegData;
	RegDataSize := rds8;
end;

{ TReadRegDataItem }

function TReadRegDataItem.CalcPayload: AnsiString;
begin
	Result := AnsiChar(RegOff);
end;

constructor TReadRegDataItem.Create;
begin
	fDID := DIDReadReg;
end;

class function TReadRegDataItem.OKReplyType: TDataItemClass;
begin
	Result := TReadRegReplyDataItem;
end;

procedure TReadRegDataItem.SetPayload(NewPayload: AnsiString);
begin
	MinPayloadExcept(Length(NewPayload), 1);
	RegOff := Byte(NewPayload[1]);
end;

{ TReadRegReplyDataItem }

constructor TReadRegReplyDataItem.Create;
begin
	fDID := DIDReadReg;
end;

{ TWriteRegDataItem }

constructor TWriteRegDataItem.Create;
begin
	fDID := DIDWriteReg;
end;

class function TWriteRegDataItem.OKReplyType: TDataItemClass;
begin
	Result := TWriteRegReplyDataItem;
end;

{ TWriteRegReplyDataItem }

constructor TWriteRegReplyDataItem.Create;
begin
	fDID := DIDWriteReg;
end;

{ TADCStreamStartDataItem }

function TADCStreamStartDataItem.CalcPayload: AnsiString;
begin
	SetString(Result, PAnsiChar(@ConnectionID), SizeOf(ConnectionID));
end;

constructor TADCStreamStartDataItem.Create;
begin
	fDID := DIDADCStreamStart;
end;

procedure TADCStreamStartDataItem.SetPayload(NewPayload: AnsiString);
begin
	MinPayloadExcept(Length(NewPayload), SizeOf(ConnectionID));
	ConnectionID := PLongWord(@NewPayload[1])^;
end;

{ TADCStreamStopDataItem }

constructor TADCStreamStopDataItem.Create;
begin
	fDID := DIDADCStreamStop;
end;

{ TConnectionID }

function TConnectionIDDataItem.CalcPayload: AnsiString;
begin
	SetString(Result, PAnsiChar(@ConnectionID), SizeOf(ConnectionID));
end;

constructor TConnectionIDDataItem.Create;
begin
  fDID := DIDHelloConnectionID;
end;

procedure TConnectionIDDataItem.SetPayload(NewPayload: AnsiString);
begin
	MinPayloadExcept(Length(NewPayload), SizeOf(ConnectionID));
	ConnectionID := PLongWord(@NewPayload[1])^;
end;

{ TDACRange1 }

function TDACRange1DataItem.CalcPayload: AnsiString;
begin
	SetString(Result, nil, SizeOf(Ch) + SizeOf(RangeCode));
	Result[1] := AnsiChar(Ch);
	PLongWord(@Result[2])^ := RangeCode;
end;

constructor TDACRange1DataItem.Create;
begin
	fDID := DIDDACRange;
end;

procedure TDACRange1DataItem.SetPayload(NewPayload: AnsiString);
begin
	MinPayloadExcept(Length(NewPayload), SizeOf(Ch) + SizeOf(RangeCode));
	Ch := Byte(NewPayload[1]);
	RangeCode := PLongWord(@NewPayload[2])^;
end;

{ TDeviceID }

function TDeviceIDDataItem.CalcPayload: AnsiString;
begin
	SetString(Result, nil, SizeOf(DeviceID));
	PWord(@Result[1])^ := DeviceID;
end;

constructor TDeviceIDDataItem.Create;
begin
	fDID := DIDHelloDeviceID;
end;

procedure TDeviceIDDataItem.SetPayload(NewPayload: AnsiString);
begin
	MinPayloadExcept(Length(NewPayload), SizeOf(DeviceID));
	DeviceID := PWord(@NewPayload[1])^;
end;

{ TFeatures }

function TFeaturesDataItem.CalcPayload: AnsiString;
begin
	SetString(Result, nil, 1);
	PByte(@Result[1])^ := Features;
end;

constructor TFeaturesDataItem.Create;
begin
	fDID := DIDHelloFeatures;
end;

procedure TFeaturesDataItem.SetPayload(NewPayload: AnsiString);
begin
	MinPayloadExcept(Length(NewPayload), 1);
	Features := Byte(NewPayload[1]);
end;

{ TFPGAVer }

function TFPGAVerDataItem.CalcPayload: AnsiString;
begin
	SetString(Result, nil, SizeOf(Ver));
	PLongWord(@Result[1])^ := Ver;
end;

constructor TFPGAVerDataItem.Create;
begin
	fDID := DIDHelloFPGAVer;
end;

procedure TFPGAVerDataItem.SetPayload(NewPayload: AnsiString);
begin
	MinPayloadExcept(Length(NewPayload), SizeOf(Ver));
	Ver := PLongWord(@NewPayload[1])^;
end;

{ TBaseClock }

function TBaseClockDataItem.CalcPayload: AnsiString;
begin
	SetString(Result, nil, SizeOf(BaseClock));
	PLongWord(@Result[1])^ := BaseClock;
end;

constructor TBaseClockDataItem.Create;
begin
	fDID := DIDHelloBaseClock;
end;

procedure TBaseClockDataItem.SetPayload(NewPayload: AnsiString);
begin
	MinPayloadExcept(Length(NewPayload), SizeOf(BaseClock));
	BaseClock := PLongWord(@NewPayload[1])^;
end;

{ THostnameDataItem }

function THostnameDataItem.CalcPayload: AnsiString;
begin
	Result := Hostname;
end;

constructor THostnameDataItem.Create;
begin
	fDID := DIDHostname;
end;

procedure THostnameDataItem.SetPayload(NewPayload: AnsiString);
begin
	Hostname := NewPayload;
end;

{ TCalFactor1DataItem }

function TCalFactor1DataItem.CalcPayload: AnsiString;
begin
	SetString(Result, nil, 1 + SizeOf(Factor));
	Result[1] := AnsiChar(Ch);
	PSingle(@Result[2])^ := Factor;
end;

procedure TCalFactor1DataItem.SetPayload(NewPayload: AnsiString);
begin
	MinPayloadExcept(Length(NewPayload), 1 + SizeOf(Factor));
	Ch := Byte(NewPayload[1]);
	Factor := PSingle(@NewPayload[2])^;
end;

{ TCalScale1DataItem }

constructor TCalScale1DataItem.Create;
begin
	fDID := DIDDACScale1;
end;

{ TCalOffset1DataItem }

constructor TCalOffset1DataItem.Create;
begin
	fDID := DIDDACOffset1;
end;

{ TCalFactorAllDataItem }

function TCalFactorAllDataItem.CalcPayload: AnsiString;
var
	I: Integer;

begin
	SetString(Result, nil, SizeOf(Factor[0]) * Count);
	for I := 0 to Count-1 do
		PSingle(@Result[1 + I*SizeOf(Factor[0])])^ := Factor[I]
	;
end;

function TCalFactorAllDataItem.GetFactor(I: Integer): Single;
begin
	if (I < 0) or (I > High(Factor)) then raise ERangeError.Create(IntToStr(I) + ' is an invalid index to TCalFactorAllDataItem.GetFactor.');

	Result := Factor[I];
end;

procedure TCalFactorAllDataItem.SetFactor(I: Integer; NewFactor: Single);
begin
	if (I < 0) or (I > High(Factor)) then raise ERangeError.Create(IntToStr(I) + ' is an invalid index to TCalFactorAllDataItem.SetFactor.');

	Factor[I] := NewFactor;
end;

function TCalFactorAllDataItem.GetCount: Integer;
begin
	Result := Length(Factor);
end;

procedure TCalFactorAllDataItem.SetCount(NewCount: Integer);
begin
	SetLength(Factor, NewCount);
end;

procedure TCalFactorAllDataItem.SetPayload(NewPayload: AnsiString);
var
	I: Integer;

begin
	SetLength(Factor, Length(NewPayload) div SizeOf(Factor[0]));
	for I := 0 to High(Factor) do
		Factor[I] := PSingle(@NewPayload[1 + I*SizeOf(Factor[0])])^
	;
end;

{ TCalScaleAllDataItem }

constructor TCalScaleAllDataItem.Create;
begin
	fDID := DIDDACScaleAll;
end;

{ TCalOffsetAllDataItem }

constructor TCalOffsetAllDataItem.Create;
begin
	fDID := DIDDACOffsetAll;
end;

initialization
	RegisterCommandReplyPairDID(DIDHelloConnectionID, TConnectionIDDataItem, TConnectionIDDataItem);
	RegisterCommandReplyPairDID(DIDReadReg, TReadRegDataItem, TReadRegReplyDataItem);
	RegisterCommandReplyPairDID(DIDWriteReg, TWriteRegDataItem, TWriteRegReplyDataItem);
	RegisterCommandReplyPairDID(DIDADCStreamStart, TADCStreamStartDataItem, TADCStreamStartDataItem);
	RegisterCommandReplyPairDID(DIDADCStreamStop, TADCStreamStopDataItem, TADCStreamStopDataItem);
	RegisterCommandReplyPairDID(DIDHelloConnectionID, TConnectionIDDataItem, TConnectionIDDataItem);
	RegisterCommandReplyPairDID(DIDDACRange, TDACRange1DataItem, TDACRange1DataItem);
	RegisterCommandReplyPairDID(DIDHelloDeviceID, TDeviceIDDataItem, TDeviceIDDataItem);
	RegisterCommandReplyPairDID(DIDHelloFeatures, TFeaturesDataItem, TFeaturesDataItem);
	RegisterCommandReplyPairDID(DIDHelloFPGAVer, TFPGAVerDataItem, TFPGAVerDataItem);
	RegisterCommandReplyPairDID(DIDHelloBaseClock, TBaseClockDataItem, TBaseClockDataItem);
	RegisterCommandReplyPairDID(DIDHelloBaseClock, THostnameDataItem, THostnameDataItem);
end.