unit uRotorIFDynamicOld;
// Dynamic Load
{ Interface unit for J.R. Kerr NMCLIB04.DLL NOW Rotor.DLL }
// Hello Big Jim...  The entry points are  C A S E   S E N S I T I V E  // 2012-07-22 Changed pchar to pAnsichar in SioOpen and get/put

interface

uses
  Winapi.Windows, Winapi.Messages, SysUtils, Vcl.Dialogs, uSETITypes;

type
  TErrorMsgBoxFunc = function(Text: PAnsiChar): boolean; stdCall;
  TIoGetADCValFunc = function(portname: Byte; Chan: Byte): Byte; stdcall;

{$REGION '-- C O N S T A N T S ----- }

Const
  AzPulsePerDeg = 628; // Rounded up to an integer
  AzVelocity = 50000; // 100000
  AzAcceleration = 100; // 20; // back to 20 2009-03-09
  ElPulsePerDeg = 2494;

  ElVelocity = 100000;
  ElAcceleration = 50; //
  RotorDLL = 'Rotor.DLL';
  // RotorDLL = 'NMCLIB04.DLL';
  CNumKerrModules = 3;
  FilterRelay = 2; // Channel on the Head End electronics Kerr module
  ElAddr = 2;
  AzAddr = 3;
  IOAddr = 1;
  AzPWM = 75; // For rotor homing (was 100)
  ElPWM = 250; // For rotor homing
  ElKp = 100;
  ElKd = 1000;
  ElKi = 0;
  ElIl = 0;
  ElEl = 16000;

  ElOl = 255;
  ElCl = 0;
  ElSr = 1;
  ElDc = 0; // Dead band compensation
  ElSm = 1;

  AzMax = 360;
  AzKp = 100;
  AzKd = 1000;
  AzKi = 0;
  AzIl = 0;
  AzEl = 16000;

  AzOl = 255;
  AzCl = 0;
  AzSr = 1;
  AzDc = 0; // Dead band compensaton was 45
  AzSm = 1;

  // Module type definitions:
  SERVOMODTYPE = 0;
  ADCMODTYPE = 1;
  IOMODTYPE = 2;
  STEPMODTYPE = 3;

  // Servo Module Command set:
  RESET_POS = $00; // Reset encode counter to 0
  SET_ADDR = $01; // Set address and group address
  DEF_STAT = $02; // Define status item to return
  READ_STAT = $03; // Read value of current status items
  LOAD_TRAJ = $04; // Load trajectory data
  START_MOVE = $05; // Start pre-loaded trajectory move
  SET_GAIN = $06; // Set servo gain and contro parameters
  STOP_MOTOR = $07; // Stop motor
  IO_CTRL = $08; // Define bit directions and set output
  SET_HOMING = $09; // Define homing mode
  SET_BAUD = $0A; // Set the baud rate
  CLEAR_BITS = $0B; // Save current position in the home position register
  SAVE_AS_HOME = $0C; // Store the input bytes and timer value
  // $ 0D   Not used
  NOP = $0E; // No Op - returnes previous defined status
  HARD_RESET = $0F; // RESET - no status is returned

  // IO modules STATUSITEMS bit definitions
  SEND_AD1 = $02;
  SEND_AD2 = $04;
  SEND_AD3 = $08;

  // Servo Module STATUSITEMS bit definitions
  SEND_POS = $01;
  SEND_AD = $02;
  SEND_VEL = $04;
  SEND_AUX = $08;
  SEND_HOME = $10;
  SEND_ID = $20;

  // Servo Module LOAD_TRAJ control byte bit definitions
  LOAD_POS = $01;
  LOAD_VEL = $02;
  LOAD_ACC = $04;
  LOAD_PWM = $08;
  ENABLE_SERVO = $10;
  VEL_MODE = $20;
  REVERSE = $40;
  MOVE_REL = $40;
  START_NOW = $80;

  // Servo Module STOP_MOTOR control byte bit definitions:
  AMP_ENABLE = $01; // Enables the amplifier
  MOTOR_OFF = $02; // Turns the motor off
  STOP_ABRUPT = $04; // Slam on the brakes.
  STOP_SMOOTH = $08; // Decellerate smoothly
  STOP_HERE = $10; // Stop at position

  // Servo Module IO_CTRL control byte bit definitions:
  SET_OUT1 = $01; // Set limit1 output (clear other wise)
  SET_OUT2 = $02; // Set limit2 output (clear other wise)
  IO1_IN = $04; // Limit 1 is an input (otherwise its an output)
  IO2_IN = $08; // Limit 2 is an input (otherwise its an output)

  LIMSTOP_OFF = $04; // turn off motor on limit
  LIMSTOP_ABRUPT = $08; // stop abruptly on limit
  THREE_PHASE = $10; // 1 = 3-phase mode, 0 = single PWM channel
  ANTIPHASE = $20; // 1 = antiphase (0 = 50% duty cycle), 0 = PWM & dir
  FAST_PATH = $408; // 0 = 30 or 60 Hz path execution, 1 = 60 or 120 Hz
  STEP_MODE = $808; // 0 = normal operation, 1 = Step & Direction enabled

  // Servo Module SET_HOMING control byte bit definitions:
  ON_LIMIT1 = $01; // Home on change in limit1
  ON_LIMIT2 = $02; // Home on change in limit2
  HOME_MOTOR_OFF = $04; // Turn motor off when homed
  ON_INDEX = $08; // Home on change in index
  HOME_STOP_ABRUPT = $10; // Stop abruptlyh when homed
  HOME_STOP_SMOOTH = $20; // Stop smoothly when homed
  ON_POS_ERR = $40; // home on excessive position error
  ON_CUR_ERR = $80; // home on overcurrent error

  // Servo Module STATUS byte bit definitions:
  MOVE_DONE = $01; // set when move done (in Trap) when goal reached (in VEL0
  CKSUM_ERROR = $02; // checksum error received in command
  OVERCURRENT = $04; // Overcurrent detected (sticky bit)
  POWER_ON = $08; // Motor power is on
  POS_ERR = $10; // Set on excess positon error (sticky bit)
  LIMIT1 = $20; // Limit Switch 1 (on or off)
  LIMIT2 = $40; // Limit Switch 2 (on or off)
  HOME_IN_PROG = $80; // Set if searching for home.  Reset when found

  // Servo Module  Auxiliary STATUS byte bit definitions:
  INDEX = $01; // Value of the encoder index signal
  POS_WRAP = $02; // Set when 32 bit position counter wraps (sticky bit)
  SERVO_ON = $04; // Set when positon servo is operating
  ACCEL_DONE = $08; // Set when ecceleration portion of a move is done
  SLEW_DONE = $10; // Set whe slew portion of a move is done
  SERVO_OVERRUN = $20;
  // Set of servo takes longer than the specified servo period
  PATH_MODE = $40; // Set when path mode motion is in progress.
{$ENDREGION}
  //
function getAzMoveDone: boolean;
function getElMoveDone: boolean;
procedure setElevation(value: TDegrees);
procedure setAzimuth(value: TDegrees);
procedure setElevationTrajectory(value: Double);
procedure setAzimuthTrajectory(value: Double);
procedure TurnOn(value: boolean);
procedure setFilter(value: boolean);
function getFilter: boolean;
function getAzMotorPosition: integer;
procedure setAzMotorPosition(value: integer);
function getElMotorPosition: integer;
procedure setElMotorPosition(value: integer);

function setHome: boolean;
function getHome: boolean;
function getLibrary: boolean;
function Initialize(ComName: string; BaudRate: integer): boolean;

//function IoGetADCVal(portname: Byte; Chan: Byte): Byte; stdcall;
function IoGetPWMVal(portname: Byte; Chan: Byte): Byte; stdCall;
function IoSetPWMVal(portname: Byte; Pwm1: Byte; Pwm2: Byte): integer; stdCall;
function IoBitDirIn(portname: Byte; bitnum: integer): integer; stdCall;
function IoBitDirOut(portname: Byte; bitnum: integer): integer; stdCall;
function IoSetOutBit(portname: Byte; bitnum: integer): integer; stdCall;
function IoClrOutBit(portname: Byte; bitnum: integer): integer; stdCall;
function IoGetBitDir(portname: Byte; bitnum: integer): integer; stdCall;
function IoOutBitVal(portname: Byte; bitnum: integer): integer; stdCall;

procedure ErrorPrinting(f: integer)stdcall; // 1=print,0=No Print
function ErrorMsgBox(Text: PAnsiChar; out ErrorMessage: String): boolean;

function SimpleMsgBox(Text: PAnsiChar): integer; stdCall;

function SioOpen(portname: PAnsiChar; BaudRate: integer): integer; stdCall;
// Returns a handle to the PIC card
function SioPutChars(Handle: integer; data: PAnsiChar; num: integer)
  : boolean; stdCall;
function SioGetChars(Handle: integer; data: PAnsiChar; num: integer)
  : Double; stdCall;

function NmcInit(portname: PAnsiChar; BaudRate: integer): integer; stdCall;
function NmcHardReset(addr: Byte): integer; stdCall;
function NmcSendcmd(addr: Byte; cmd: Byte; datastr: pchar; n: Byte;
  stataddr: Byte): boolean; stdCall;
procedure NmcShutdown; stdCall;
function NmcReadStatus(addr: Byte; statusitems: Byte): boolean; stdCall;
function NmcDefineStatus(addr: Byte; statusitems: Byte): boolean; stdCall;
function NmcGetStat(addr: Byte): Byte; stdCall;
function NmcGetModType(addr: Byte): Byte; stdCall;
function NmcGetModVer(addr: Byte): Byte; stdCall;
function NmcNoOp(addr: Byte): boolean; stdCall;
function NmcGetStatItems(addr: Byte): Byte; stdCall;

function ServoSetPos(addr: Byte; pos: integer): boolean; stdCall;
function ServoClearBits(addr: Byte): boolean; // Does not seem to work
function ServoGetPos(addr: Byte): LongInt; stdCall;
function ServoGetPError(addr: Byte): integer; stdCall;
function ServoGetIoCtrl(addr: Byte): Byte; stdCall;
function ServoGetStat(addr: Byte): boolean; stdCall;
function ServoGetAux(addr: Byte): Byte; stdCall;
function ServoLoadTraj(addr: Byte; mode: Byte; pos: LongInt; vel: LongInt;
  acc: LongInt; pwm: Byte): boolean; stdCall;

function ServoResetPos(addr: Byte): boolean; stdCall;
function ServoSetHoming(addr: Byte; mode: Byte): boolean; stdCall;
function ServoSetIoCtrl(addr, mode: Byte): boolean; stdCall;
function ServoSetGain(addr: Byte; kp: smallInt; kd: smallInt; ki: smallInt;
  il: smallInt; ol: Byte; cl: Byte; el: smallInt; sr: Byte; dc: Byte)
  : boolean; stdCall;
function ServoSetGain2(addr: Byte; kp: smallInt; kd: smallInt; ki: smallInt;
  il: smallInt; ol: Byte; cl: Byte; el: smallInt; sr: Byte; dc: Byte; sm: Byte)
  : integer; stdCall;
function ServoStopMotor(addr: Byte; mode: Byte): boolean; stdCall;
function NumKerrModules: integer;

implementation

var
  dllHandle: cardinal;
  LastElMotorPosition: integer;
  LastAzMotorPosition: integer;
  FFilter: boolean;

function getAzMoveDone: boolean;
var
  current_status_byte: Byte;
  move_complete: Byte;

begin
  result := false;
  NmcNoOp(AzAddr);
  current_status_byte := NmcGetStat(AzAddr);
  move_complete := current_status_byte and MOVE_DONE;
  if move_complete = MOVE_DONE then
    result := true

end;

function getElMoveDone: boolean;
var
  current_status_byte: Byte;
  move_complete: Byte;

begin
  result := false;
  NmcNoOp(ElAddr);
  current_status_byte := NmcGetStat(ElAddr);
  move_complete := current_status_byte and MOVE_DONE;
  if move_complete = MOVE_DONE then
    result := true;

end;

procedure setElevation(value: TDegrees);
begin
  //
end;

procedure setAzimuth(value: TDegrees);
begin
  //
end;

procedure setElevationTrajectory(value: Double);
var
  Elevation: integer;
begin
  Elevation := Trunc(value) * ElPulsePerDeg;
  ServoLoadTraj(ElAddr, LOAD_POS or LOAD_VEL or LOAD_ACC or ENABLE_SERVO or
    START_NOW, Elevation, ElVelocity, ElAcceleration, ElPWM);

end;

procedure setAzimuthTrajectory(value: Double);
var
  Azimuth: integer;
begin
  Azimuth := Trunc(value) * AzPulsePerDeg;
  ServoLoadTraj(AzAddr, LOAD_POS or LOAD_VEL or LOAD_ACC or ENABLE_SERVO or
    START_NOW, Azimuth, AzVelocity, AzAcceleration, AzPWM);

end;

procedure TurnOn(value: boolean);
var
  i: integer;
begin
  if value = true then
  begin
    ServoStopMotor(AzAddr, AMP_ENABLE or MOTOR_OFF);
    ServoStopMotor(ElAddr, AMP_ENABLE or MOTOR_OFF);

    for i := 0 to 11 do // Set all the Kerr IO bits to output for the D-to-A
      IoBitDirOut(IOAddr, i);
  end
  else
  begin
    ErrorPrinting(0);
    NmcShutdown;
    // NmcHardReset($FF);
  end;

end;

procedure setFilter(value: boolean);
var
  CurrentFilter: Byte;
begin
  CurrentFilter := IoGetPWMVal(IOAddr, FilterRelay);

  if (value = true) then
    IoSetPWMVal(IOAddr, CurrentFilter, 0)
  else
    IoSetPWMVal(IOAddr, CurrentFilter, 255);
  FFilter := value;
end;

function getFilter: boolean;
begin
  result := FFilter;
end;

function getAzMotorPosition: integer;
var
  pos: integer;
begin

  begin
    NmcReadStatus(AzAddr, SEND_POS or SEND_VEL);
    pos := ServoGetPos(AzAddr);

    result := ServoGetPos(AzAddr);

  end

end;

procedure setAzMotorPosition(value: integer);
begin
  ServoSetPos(AzAddr, value);
end;

function getElMotorPosition: integer;

begin
  NmcReadStatus(ElAddr, SEND_POS or SEND_VEL);
  result := ServoGetPos(ElAddr);
end;

procedure setElMotorPosition(value: integer);
begin
  ServoSetPos(ElAddr, value);
end;

function getHome: boolean;
var
  FElHome, FAzHome: boolean;
begin

  result := false;
  LastElMotorPosition := getElMotorPosition;
  if (LastElMotorPosition <> getElMotorPosition) then
    FElHome := false
  else
    FElHome := true;

  LastAzMotorPosition := getAzMotorPosition;
  if (LastAzMotorPosition <> getAzMotorPosition) then
    FAzHome := false
  else
    FAzHome := true;
  if (FAzHome AND FElHome) then
  begin
    setAzMotorPosition(0);
    // Set Motor Position to Zero to start
    setElMotorPosition(0);

    ServoResetPos(AzAddr);
    ServoResetPos(ElAddr);

    result := true;
  end;
end;

function setHome: boolean;
var
  message1, message2: string;
begin

  ServoLoadTraj(AzAddr, LOAD_PWM or START_NOW or REVERSE, 0, 0, 0, AzPWM);
  ServoLoadTraj(ElAddr, LOAD_PWM or START_NOW or REVERSE, 0, 0, 0, ElPWM);

  try
    ServoSetGain2(ElAddr, ElKp, ElKd, ElKi, ElIl, ElOl, ElCl, ElEl, ElSr,
      ElDc, ElSm);
    ServoSetGain2(AzAddr, AzKp, AzKd, AzKi, AzIl, AzOl, AzCl, AzEl, AzSr,
      AzDc, AzSm);
  except
    message1 := ('Exception class name = ');
    message2 := ('Exception message = ');
  end;

end;

function Initialize(ComName: string; BaudRate: integer): boolean;
var
  res: boolean;
  nModules: integer;
  message1, message2: string;

begin
  // ErrorPrinting(1);
  // nummod := NmcInit(Port, 19200);
  // //uAntenna.lblNumModules.Caption := IntToStr(nummod);
  //
  // exit;
  result := false;
  ErrorPrinting(0);

  If getLibrary then
    try
      // SimpleMsgBox('Test Before');
      nModules := NmcInit(PAnsiChar(AnsiString(ComName)), BaudRate);

    except
      // on E: Exception do
      begin

        // message1 := ('Exception class name = ' + E.ClassName);
        message1 := ('Exception class name = ');
        // message2 := ('Exception message = ' + E.Message);
        message2 := ('Exception message = ');
      end;

    end;

  if (nModules = CNumKerrModules) then
    // SimpleMsgBox('nModules = ');
    result := true;
end;

function getLibrary: boolean;
begin
  dllHandle := LoadLibrary(RotorDLL);
  if dllHandle <> 0 then
  begin
    result := true;
    // ErrorPrinting(0);   // Jim out for debug only
  end
  else
  begin
    result := false;
  end;
end;

function NumKerrModules: integer;
begin
  result := CNumKerrModules;
end;



procedure setTestJack(value: integer);
var
  i, Mask: integer;
  Show: string;
  Request: integer;
begin
  // if not Ready then
  // exit;
  // FTestJack := value;
  // Request := value;
  // if (Request > 4095) then
  // Request := 4095;
  //
  // Mask := $01;
  //
  // for i := 0 to 11 do
  // begin
  // if (Request and Mask) <> 0 then
  // begin
  // Show := ' 1 ' + Show;
  //
  // IoSetOutBit(AddrIO, i);
  // end
  // else
  // begin
  // Show := ' 0 ' + Show;
  //
  // IoClrOutBit(AddrIO, i);
  // end;
  // Mask := Mask shl 1;
  // end;
  // sbAntenna.Panels[csbNote].text := 'Test Jack Bits: ' + Show;
end;

function ErrorMsgBox(Text: PAnsiChar; out ErrorMessage: String): boolean;
const
  TestString = 'Test String';
var
  res: integer;
  ErrorMsgBoxFunc: TErrorMsgBoxFunc;
  PcharText: PAnsiChar;
begin
  result := false; // preset the result to error
  dllHandle := LoadLibrary('Rotor.DLL');
  if dllHandle <> 0 then
  begin
    @ErrorMsgBoxFunc := GetProcAddress(dllHandle, 'ErrorMsgBox');
    if Assigned(ErrorMsgBoxFunc) then
    begin
      result := ErrorMsgBoxFunc(Text);
    end
    else
    begin
      ErrorMessage := 'Not Assigned  ErrorMsgBoxFunc ';
      FreeLibrary(dllHandle);
    end;
  end
  else
    ErrorMessage := 'Rotor.DLL not found / not loaded';
end;

//function IoGetADCVal; external RotorDLL name 'IoGetADCVal';
 function  IoGetADCVal(Text: PAnsiChar; out ErrorMessage: String): byte;
const
  TestString = 'Test String';
var
  res: integer;
  // TIoGetADCValFunc = function(portname: Byte; Chan: Byte): Byte; stdcall;
  IoGetADCValFunc: TIoGetADCValFunc;
  PcharText: PAnsiChar;
begin

  dllHandle := LoadLibrary('Rotor.DLL');
  if dllHandle <> 0 then
  begin
    @IoGetADCValFunc := GetProcAddress(dllHandle, 'IoGetADCVal');
    if Assigned(IoGetADCValFunc) then
    begin
      result := IoGetADCValFunc(Text);
    end
    else
    begin
      ErrorMessage := 'Not Assigned  IoGetADCValFunc ';
      FreeLibrary(dllHandle);
    end;
  end
  else
    ErrorMessage := 'Rotor.DLL not found / not loaded';
end;

function getADC1: integer;

const // IO modules STATUSITEMS bit definitions
  SEND_AD1 = $02;
  SEND_AD2 = $04;
  SEND_AD3 = $08;
var
  status_items: Byte;
begin
  status_items := SEND_AD1 or SEND_AD2 or SEND_AD3;
  NmcReadStatus(IOAddr, status_items);
  result := IoGetADCVal(IOAddr, 0);
  { analog1 := IoGetADCVal(IOAddr, 1);
    analog2 := IoGetADCVal(IOAddr, 20);

    Delay(1000);
    IoGetPWMVal(AzAddr, PWMVal);
    A2DOut := IoGetADCVal(1, 1);
  }
end;


function IoGetPWMVal; external RotorDLL name 'IoGetPWMVal';
function IoSetPWMVal; external RotorDLL name 'IoSetPWMVal';
function IoBitDirIn; external RotorDLL name 'IoBitDirIn';
function IoBitDirOut; external RotorDLL name 'IoBitDirOut';
function IoSetOutBit; external RotorDLL name 'IoSetOutBit';
function IoClrOutBit; external RotorDLL name 'IoClrOutBit';
function IoGetBitDir; external RotorDLL name 'IoGetBitDir';
function IoOutBitVal; external RotorDLL name 'IoOutBitVal';

procedure ErrorPrinting; external RotorDLL name 'ErrorPrinting';

function SimpleMsgBox; external RotorDLL name 'SimpleMsgBox';
function SioOpen; external RotorDLL name 'SioOpen';
function SioPutChars; external RotorDLL name 'SioPutChars';
function SioGetChars; external RotorDLL name 'SioGetChars';

function NmcGetModType; external RotorDLL name 'NmcGetModType';
function NmcGetModVer; external RotorDLL name 'NmcGetModVer';
function ServoSetGain2; external RotorDLL name 'ServoSetGain2';
function ServoStopMotor; external RotorDLL name 'ServoStopMotor';
function NmcInit; external RotorDLL name 'NmcInit';
function NmcHardReset; external RotorDLL name 'NmcHardReset';
// -------------------------tested to here ----------------------

function ServoLoadTraj; external RotorDLL name 'ServoLoadTraj';
function ServoGetPError; external RotorDLL name 'ServoGetPError';
function ServoGetPos; external RotorDLL name 'ServoGetPos';
function ServoSetHoming; external RotorDLL name 'ServoSetHoming';
function ServoResetPos; external RotorDLL name 'ServoResetPos';

function NmcSendcmd; external RotorDLL name 'NmcSendcmd';

procedure NmcShutdown; external RotorDLL name 'NmcShutdown';
function NmcReadStatus; external RotorDLL name 'NmcReadStatus';

function NmcDefineStatus; external RotorDLL name 'NmcDefineStatus';

function NmcGetStat; external RotorDLL name 'NmcGetStat';

function NmcNoOp; external RotorDLL name 'NmcNoOp';

function NmcGetStatItems; external RotorDLL name 'NmcGetStatItems';

function ServoGetIoCtrl; external RotorDLL name 'ServoGetIoCtrl';
function ServoSetIoCtrl; external RotorDLL name 'ServoSetIoCtrl';
function ServoSetGain; external RotorDLL name 'ServoSetGain';
function ServoSetPos; external RotorDLL name 'ServoSetPos';
function ServoClearBits; external RotorDLL name 'ServoClearBits';
function ServoGetStat; external RotorDLL name 'ServoGetStat';
function ServoGetAux; external RotorDLL name 'ServoGetAux';

end.
