Click or drag to resize

4.5.8 Programming Examples

Code samples for programmatic control of an nLight Laser.

General Operating Functions.
C#
public class NLight_Laser_Example
{
    private const string CardIP = "192.168.1.1"; // Obviously, you should provide the real address of your card here.

    [Flags]
    private enum InputState
    {
        BppReady = 1 << 0,
        ExtControlReady = 1 << 1,
        WaterFlow = 1 << 2,
        Ready = 1 << 3,
        Error = 1 << 4,
        Emission = 1 << 5,
        FirmwareReady = 1 << 6
    }

    private enum DeviceStateType
    {
        NoDevice,
        Off,
        Standby,
        Emission,
        Error,
        Fault
    }

    [Flags]
    private enum NLightSignalIOPin : uint
    {
        PRO_START = 1 << 7,
        PRO_B1 = 1 << 8,
        PRO_B2 = 1 << 9,
        PRO_B3 = 1 << 10,
        PRO_B4 = 1 << 11,
        PRO_B5 = 1 << 12,
        PRO_B6 = 1 << 13,
        PRO_B7 = 1 << 14,
        SYSTEM_ON = 1 << 15,
        ENABLE_PROFILE = 1 << 16,
        ARM_LASER = 1 << 18,
        CLEAR_ERROR = 1 << 19,
        ENABLE_AIMING_LASER = 1 << 20,
        ENABLE_EXTERNAL_CONTROL = 1 << 21,
        ENABLE_24V = 1 << 22,
        GATE_IN = 1 << 23
    }

    private DeviceStateType DeviceState;

    private ClientAPI _client = new ClientAPI( CardIP );

    public bool IsInitialized => DeviceState == DeviceStateType.Standby || DeviceState == DeviceStateType.Emission;

    public bool DisarmToOffState { get; set; }

    private void InitializeAdapter()
    {
        SpiConfig spiConfig = _client.Sfio.Spi.GetConfig();
        spiConfig.Modules[2] = new SpiModule
        {
            Enabled = true,
            BitOrder = Order.MsbFirst,
            PreDelay = 0.5,
            PostDelay = 0.5,
            FrameDelay = 0.25,
            SpiSyncMode = SyncMode.SyncPerFrame,
            ClockPeriod = 0.125,
            BitsPerWord = 32,
            OutputSource = DataSource.Spi
        };
        _client.Sfio.Spi.SetConfig( spiConfig );

        // Enable 24V.
        _client.Gpio.Write( IOPort.PortD, PinAction.Set, (uint)NLightSignalIOPin.ENABLE_24V );

        // Clear special functions.
        _client.Gpio.Write( IOPort.PortD, PinAction.Clear, (uint)( NLightSignalIOPin.ARM_LASER | NLightSignalIOPin.ENABLE_AIMING_LASER | NLightSignalIOPin.GATE_IN ) );

        // Disarm the laser initially.
        ArmLaser( false );
    }

    private void InitializeLaser()
    {
        UpdateDeviceState( out InputState inputState );
        if ( DeviceState == DeviceStateType.NoDevice || IsInitialized )
            throw new Exception( "Could not detect laser" );

        bool? hasError = DeviceState == DeviceStateType.Error ? true : (bool?)null;

        // Wait for FIRMWARE_READY signal from laser.
        while ( hasError == null || hasError.Value || !inputState.HasFlag( InputState.FirmwareReady ) )
        {
            ClearError( true );
            UpdateDeviceState( out inputState );
            hasError = DeviceState == DeviceStateType.Error;

            if ( hasError.Value || !inputState.HasFlag( InputState.FirmwareReady ) )
                Thread.Sleep( TimeSpan.FromMilliseconds( 30 ) );
        }

        // Wait for EXT_CONTROL_RDY signal from laser.
        hasError = null;

        while ( hasError == null || hasError.Value || !inputState.HasFlag( InputState.ExtControlReady ) )
        {
            ClearError( true );

            if ( hasError == null || hasError.Value )
                EnableExternalControl( true );

            UpdateDeviceState( out inputState );
            hasError = DeviceState == DeviceStateType.Error;

            if ( hasError.Value || !inputState.HasFlag( InputState.ExtControlReady ) )
                Thread.Sleep( TimeSpan.FromMilliseconds( 30 ) );
        }

        // Wait for READY from laser. Min wait time is 2s.
        hasError = null;
        while ( DeviceState != DeviceStateType.Standby )
        {
            ClearError( true );

            if ( hasError == null || hasError.Value )
            {
                EnableSystem( true );
                Thread.Sleep( 2000 );
            }

            UpdateDeviceState( out inputState );

            hasError = DeviceState == DeviceStateType.Error;

            if ( DeviceState != DeviceStateType.Standby )
                Thread.Sleep( TimeSpan.FromMilliseconds( 30 ) );
        }

        // Set beam profile ID to 0. Otherwise, the state is undefined.
        hasError = null;
        while ( hasError == null || hasError.Value || !inputState.HasFlag( InputState.BppReady ) )
        {
            ClearError( true );

            if ( hasError == null || hasError.Value )
            {
                ChangeBeamProfile( 0 );
                Thread.Sleep( TimeSpan.FromMilliseconds( 40 ) );
            }

            UpdateDeviceState( out inputState );

            hasError = DeviceState == DeviceStateType.Error;

            if ( hasError.Value || !inputState.HasFlag( InputState.BppReady ) )
                Thread.Sleep( TimeSpan.FromMilliseconds( 30 ) );
        }
    }

    //////////////////////////////////////////////////////
    // 
    // Initialize MUST be called before any other methods.
    // 
    //////////////////////////////////////////////////////
    public void Initialize()
    {
        InitializeAdapter();
        InitializeLaser();
    }

    private void ClearError( bool shouldGotoOffState )
    {
        if ( DeviceState != DeviceStateType.Error )
            return;

        ArmLaser( false );

        while ( true )
        {
            if ( shouldGotoOffState )
            {
                EnableSystem( false );
                UpdateDeviceState( out InputState inputState );

                if ( DeviceState == DeviceStateType.Off )
                    break;
            }
            else
            {
                _client.Gpio.Write( IOPort.PortD, PinAction.Clear, (uint)NLightSignalIOPin.CLEAR_ERROR );
                Thread.Sleep( TimeSpan.FromMilliseconds( 1 ) );
                _client.Gpio.Write( IOPort.PortD, PinAction.Set, (uint)NLightSignalIOPin.CLEAR_ERROR );
                Thread.Sleep( TimeSpan.FromMilliseconds( 1 ) );

                Thread.Sleep( TimeSpan.FromMilliseconds( 5000 ) );
                UpdateDeviceState( out InputState inputState );

                if ( DeviceState == DeviceStateType.Standby )
                    break;
            }
            Thread.Sleep( TimeSpan.FromMilliseconds( 30 ) );
        }
    }

    private uint ReadInputs()
    {
        uint[] txData = { 0x03000000 };
        uint[] rxData = _client.Sfio.Spi.Transceive( 2, txData, 10 );
        return rxData.First();
    }

    private void UpdateDeviceState( out InputState inputState )
    {
        uint inputs = ReadInputs();
        inputState = (InputState)inputs;

        // IMPORTANT: This is an assumption that there is no actual laser available.
        if ( ( inputs & 0xFF ) == 0xFF )
        {
            Console.Out.WriteLine( "No laser present" );
            DeviceState = DeviceStateType.NoDevice;
            return;
        }

        bool isExtControlReady = inputState.HasFlag( InputState.ExtControlReady );
        bool isReady = inputState.HasFlag( InputState.Ready );
        bool isError = inputState.HasFlag( InputState.Error );
        bool isEmission = inputState.HasFlag( InputState.Emission );
        bool isFirmwareReady = inputState.HasFlag( InputState.FirmwareReady );
        bool isWaterFlow = inputState.HasFlag( InputState.WaterFlow );
        Console.Out.WriteLine( $"Current state: {inputState}" );

        if ( isError )
            DeviceState = DeviceStateType.Error;
        else
        {
            if ( isFirmwareReady && isExtControlReady )
            {
                if ( isReady )
                    DeviceState = isEmission ? DeviceStateType.Emission : DeviceStateType.Standby;
                else
                    DeviceState = DeviceStateType.Off;
            }
            else
                DeviceState = DeviceStateType.Off;
        }
    }

    public void EnableSystem( bool enable )
    {
        _client.Gpio.Write( IOPort.PortD, PinAction.Clear, (uint)NLightSignalIOPin.SYSTEM_ON );
        if ( enable )
        {
            Thread.Sleep( TimeSpan.FromMilliseconds( 1 ) );
            _client.Gpio.Write( IOPort.PortD, PinAction.Set, (uint)NLightSignalIOPin.SYSTEM_ON );
        }
    }

    public void ChangeBeamProfile( ushort profileID )
    {
        (uint setMask, uint clearMask) = CalculateBeamProfileIDMasks( profileID );
        _client.Gpio.Write( IOPort.PortD, PinAction.Set, setMask );
        _client.Gpio.Write( IOPort.PortD, PinAction.Clear, clearMask );
        _client.Gpio.Write( IOPort.PortD, PinAction.Set, (uint)NLightSignalIOPin.PRO_START );
        _client.Gpio.Write( IOPort.PortD, PinAction.Clear, (uint)NLightSignalIOPin.PRO_START );
    }

    private (uint setMask, uint clearMask) CalculateBeamProfileIDMasks( ushort profileID )
    {
        const int localMask = ( 1 << 4 ) - 1;
        uint setMask = (uint)( ( profileID & localMask ) << 8 );
        uint clearMask = (uint)( ( ~profileID & localMask ) << 8 );

        // Clear PROFILE_EN.
        clearMask |= (uint)NLightSignalIOPin.ENABLE_PROFILE;

        // Set AFX mode.
        setMask |= (uint)NLightSignalIOPin.PRO_B7;
        return (setMask, clearMask);
    }

    public void EnableExternalControl( bool enable )
    {
        _client.Gpio.Write( IOPort.PortD, PinAction.Set, (uint)NLightSignalIOPin.ENABLE_EXTERNAL_CONTROL );

        if ( !enable )
        {
            Thread.Sleep( TimeSpan.FromMilliseconds( 1 ) );
            _client.Gpio.Write( IOPort.PortD, PinAction.Clear, (uint)NLightSignalIOPin.ENABLE_EXTERNAL_CONTROL );
        }
    }

    private bool GetLaserArmed()
    {
        UpdateDeviceState( out InputState _ );
        return DeviceState == DeviceStateType.Emission && _client.Laser.IsLaserArmed();
    }

    public void ArmLaser( bool enable )
    {
        bool isArmed = GetLaserArmed();

        if ( DeviceState == DeviceStateType.NoDevice || enable && DeviceState == DeviceStateType.Emission || !enable && !isArmed && DeviceState != DeviceStateType.Error && DeviceState != DeviceStateType.Emission )
            return;

        if ( enable )
        {
            try
            {
                if ( DeviceState != DeviceStateType.Standby )
                    Initialize();
            }
            catch ( Exception )
            {
                // ignored
            }

            if ( DeviceState != DeviceStateType.Standby )
                throw new ApplicationException( "Cannot initialize laser during arming process." );
        }

        // Wait EMISS. Min time is 2s.

        UpdateDeviceState( out InputState _ );
        bool? hasError = DeviceState == DeviceStateType.Error ? true : (bool?)null;

        while ( hasError == null || hasError.Value || enable && DeviceState != DeviceStateType.Emission || !enable && DeviceState == DeviceStateType.Emission )
        {
            ClearError( false );

            if ( hasError == null || hasError.Value )
            {
                _client.Laser.ArmLaser( enable );

                if ( enable )
                    Thread.Sleep( TimeSpan.FromMilliseconds( 2000 ) );
            }

            UpdateDeviceState( out InputState _ );
            hasError = DeviceState == DeviceStateType.Error;

            if ( hasError.Value || enable && DeviceState != DeviceStateType.Emission || !enable && DeviceState == DeviceStateType.Emission )
                Thread.Sleep( TimeSpan.FromMilliseconds( 30 ) );
        }

        if ( !enable && DisarmToOffState )
        {
            // Wait RDY.
            UpdateDeviceState( out InputState inputState );
            hasError = DeviceState == DeviceStateType.Error ? true : (bool?)null;

            while ( hasError == null || hasError.Value || inputState.HasFlag( InputState.Ready ) )
            {
                ClearError( true );

                if ( hasError == null || hasError.Value )
                    EnableSystem( false );

                UpdateDeviceState( out inputState );
                hasError = DeviceState == DeviceStateType.Error;

                if ( hasError.Value || inputState.HasFlag( InputState.Ready ) )
                    Thread.Sleep( TimeSpan.FromMilliseconds( 30 ) );
            }
        }
    }
}