/************************************************
 *                                              *
 *      MICRO EPSILON SCAN CONTROL 2700         *
 *      Driver class                            *
 *                                              *
 *      Adam Chromy                             *
 *      version: 1.2.0                          *
 *      last modified: 27.11.2012               *
 *                                              *
 ************************************************/

#region CHANGELOG

/*
 * 
 * VERSION 1.1.0
 * NEW - Driver now implements IScanner interface, back compatibility conserved
 * 
 * VERSION 1.1.1
 * EDIT - GetValues return null in error
 * 
 * VERSION 1.2.0
 * NEW - MeasuredData (in XZPoint format) contains also reflection information
 * 
 */

#endregion

using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Drawing;

namespace Sensors.Scanners.MicroEpsilon
{

    public class scanCONTROL2750: IscanCONTROL
    {
        #region class definitions

        /// <summary>Provides device parameters packed in one object</summary>
        public class SensorInformation
        {
            public InterfaceType Interface;
            public uint Resolution;
            public uint PacketDelay;
            public ScannerType DeviceType;
            public string DeviceName;
            public uint[] FirmwareVersions;
            public uint SerialNumber;
            public uint ShutterTime;
            public uint IdleTime;
            public bool AutoShutter;
            public bool AmbientLightSupression;
            public bool VideoFilter;
            public uint ReflectionThreshold;
            public bool InvalidPointsFilter;
            public LaserModes LaserPower;
            public XRanges XRange;
            public NumSamples MedianFilter;
            public NumSamples AverageFilter;

            public SensorInformation(scanCONTROL2750 scan)
            {
                Interface = scan.Interface;
                Resolution = scan.Resolution;
                PacketDelay = scan.PacketDelay;
                DeviceType = scan.DeviceType;
                DeviceName = scan.DeviceName;
                FirmwareVersions = scan.FirmwareVersions;
                SerialNumber = scan.SerialNumber;
                ShutterTime = scan.ShutterTime;
                IdleTime = scan.IdleTime;
                AutoShutter = scan.AutoShutter;
                AmbientLightSupression = scan.AmbientLightSupression;
                VideoFilter = scan.VideoFilter;
                ReflectionThreshold = scan.ReflectionThreshold;
                InvalidPointsFilter = scan.InvalidPointsFilter;
                LaserPower = scan.LaserPower;
                XRange = scan.XRange;
                MedianFilter = scan.MedianFilter;
                AverageFilter = scan.AverageFilter;
            }
        }

        /// <summary>Encapsulates API functions from DLL library</summary>
        private class dllAPI
        {
            public const uint CONVERT_X = 0x00000800;
            public const uint CONVERT_Z = 0x00001000;
            public const uint CONVERT_WIDTH = 0x00000100;
            public const uint CONVERT_MAXIMUM = 0x00000200;

            //Function defines for the Get/SetFeature function
            public const uint FEATURE_FUNCTION_SERIAL = 0xf0000410;
            public const uint FEATURE_FUNCTION_LASERPOWER = 0xf0f00824;
            public const uint INQUIRY_FUNCTION_LASERPOWER = 0xf0f00524;
            public const uint FEATURE_FUNCTION_MEASURINGFIELD = 0xf0f00880;
            public const uint INQUIRY_FUNCTION_MEASURINGFIELD = 0xf0f00580;
            public const uint FEATURE_FUNCTION_TRIGGER = 0xf0f00830;
            public const uint INQUIRY_FUNCTION_TRIGGER = 0xf0f00530;
            public const uint FEATURE_FUNCTION_SHUTTERTIME = 0xf0f0081c;
            public const uint INQUIRY_FUNCTION_SHUTTERTIME = 0xf0f0051c;
            public const uint FEATURE_FUNCTION_IDLETIME = 0xf0f00800;
            public const uint INQUIRY_FUNCTION_IDLETIME = 0xf0f00500;
            public const uint FEATURE_FUNCTION_PROCESSING_PROFILEDATA = 0xf0f00804;
            public const uint INQUIRY_FUNCTION_PROCESSING_PROFILEDATA = 0xf0f00504;
            public const uint FEATURE_FUNCTION_THRESHOLD = 0xf0f00810;
            public const uint INQUIRY_FUNCTION_THRESHOLD = 0xf0f00510;
            public const uint FEATURE_FUNCTION_MAINTENANCEFUNCTIONS = 0xf0f0088c;
            public const uint INQUIRY_FUNCTION_MAINTENANCEFUNCTIONS = 0xf0f0058c;
            public const uint FEATURE_FUNCTION_ANALOGFREQUENCY = 0xf0f00828;
            public const uint INQUIRY_FUNCTION_ANALOGFREQUENCY = 0xf0f00528;
            public const uint FEATURE_FUNCTION_ANALOGOUTPUTMODES = 0xf0f00820;
            public const uint INQUIRY_FUNCTION_ANALOGOUTPUTMODES = 0xf0f00520;
            public const uint FEATURE_FUNCTION_CMMTRIGGER = 0xf0f00888;
            public const uint INQUIRY_FUNCTION_CMMTRIGGER = 0xf0f00588;
            public const uint FEATURE_FUNCTION_REARRANGEMENT_PROFILE = 0xf0f0080c;
            public const uint INQUIRY_FUNCTION_REARRANGEMENT_PROFILE = 0xf0f0050c;
            public const uint FEATURE_FUNCTION_PROFILE_FILTER = 0xf0f00818;
            public const uint INQUIRY_FUNCTION_PROFILE_FILTER = 0xf0f00518;
            public const uint FEATURE_FUNCTION_RS422_INTERFACE_FUNCTION = 0xf0f008c0;
            public const uint INQUIRY_FUNCTION_RS422_INTERFACE_FUNCTION = 0xf0f005c0;

            public const uint FEATURE_FUNCTION_SATURATION = 0xf0f00814;
            public const uint INQUIRY_FUNCTION_SATURATION = 0xf0f00514;
            public const uint FEATURE_FUNCTION_TEMPERATURE = 0xf0f0082c;
            public const uint INQUIRY_FUNCTION_TEMPERATURE = 0xf0f0052c;
            public const uint FEATURE_FUNCTION_CAPTURE_QUALITY = 0xf0f008c4;
            public const uint INQUIRY_FUNCTION_CAPTURE_QUALITY = 0xf0f005c4;
            public const uint FEATURE_FUNCTION_SHARPNESS = 0xf0f00808;
            public const uint INQUIRY_FUNCTION_SHARPNESS = 0xf0f00508;
            public const uint FEATURE_FUNCTION_PACKET_DELAY = 0x00000d08;

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_CreateLLTDevice",
                CallingConvention = CallingConvention.StdCall)]
            public static extern uint CreateLLTDevice(InterfaceType iInterfaceType);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_DelDevice",
                CallingConvention = CallingConvention.StdCall)]
            public static extern int DelDevice(uint pLLT);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_Connect",
                CallingConvention = CallingConvention.StdCall)]
            public static extern int Connect(uint pLLT);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_Disconnect",
                CallingConvention = CallingConvention.StdCall)]
            public static extern int Disconnect(uint pLLT);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_IsTransferingProfiles",
                CallingConvention = CallingConvention.StdCall)]
            public static extern int IsTransferingProfiles(uint pLLT);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_GetDeviceInterfaces",
                CallingConvention = CallingConvention.StdCall)]
            public static extern int GetDeviceInterfaces(uint pLLT, uint[] pInterfaces, int nSize);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_GetInterfaceType",
                CallingConvention = CallingConvention.StdCall)]
            public static extern InterfaceType GetInterfaceType(uint pLLT);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_SetDeviceInterface",
                CallingConvention = CallingConvention.StdCall)]
            public static extern int SetDeviceInterface(uint pLLT, uint nInterface, int nAdditional);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_GetDeviceName",
                CallingConvention = CallingConvention.StdCall)]
            public static extern int GetDeviceName(uint pLLT, byte[] pDevName, int nDevNameSize, byte[] pVendName, int nVenNameSize);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_GetLLTType",
                CallingConvention = CallingConvention.StdCall)]
            public static extern int GetLLTType(uint pLLT, ref ScannerType ScannerType);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_GetLLTVersions",
                CallingConvention = CallingConvention.StdCall)]
            public static extern int GetLLTVersions(uint pLLT, ref uint DSP, ref uint FPGA1, ref uint FPGA2);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_TranslateErrorValue",
              CallingConvention = CallingConvention.StdCall)]
            public static extern int TranslateErrorValue(uint pLLT, int ErrorValue, byte[] pString, int nStringSize);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_GetResolutions",
              CallingConvention = CallingConvention.StdCall)]
            public static extern int GetResolutions(uint pLLT, uint[] pValue, int nSize);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_GetFeature",
              CallingConvention = CallingConvention.StdCall)]
            public static extern int GetFeature(uint pLLT, uint Function, ref uint pValue);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_SetFeature",
              CallingConvention = CallingConvention.StdCall)]
            public static extern int SetFeature(uint pLLT, uint Function, uint Value);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_SetResolution",
              CallingConvention = CallingConvention.StdCall)]
            public static extern int SetResolution(uint pLLT, uint Value);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_GetResolution",
              CallingConvention = CallingConvention.StdCall)]
            public static extern int GetResolution(uint pLLT, ref uint Value);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_SetProfileConfig",
              CallingConvention = CallingConvention.StdCall)]
            public static extern int SetProfileConfig(uint pLLT, ProfileConfig Value);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_TransferProfiles",
              CallingConvention = CallingConvention.StdCall)]
            public static extern int TransferProfiles(uint pLLT, TransferProfileType TransferProfileType, int nEnable);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_ConvertProfile2Values",
              CallingConvention = CallingConvention.StdCall)]
            public static extern int ConvertProfile2Values(uint pLLT, byte[] pProfile, uint nResolution,
              ProfileConfig ProfileConfig, ScannerType ScannerType, uint nReflection, int nConvertToMM,
              ushort[] pWidth, ushort[] pMaximum, ushort[] pThreshold, double[] pX, double[] pZ,
              uint[] pM0, uint[] pM1);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_GetActualProfile",
               CallingConvention = CallingConvention.StdCall)]
            public static extern int GetActualProfile(uint pLLT, byte[] pBuffer, uint nBuffersize,
              ProfileConfig ProfileConfig, ref uint pLostProfiles);

            [DllImport(DRIVER_DLL_NAME, EntryPoint = "s_Timestamp2TimeAndCount",
               CallingConvention = CallingConvention.StdCall)]
            public static extern int Timestamp2TimeAndCount(byte[] pBuffer, ref double dTimeShutterOpen, ref double dTimeShutterClose, ref uint uiProfileCount);

        }

        [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
        public unsafe delegate int SR_FuncCB(IntPtr srCam, uint msg, uint param, string data);

        #endregion

        #region constants

        /// <summary>Max. count of simultaneously connected devices</summary>
        public const int MAX_INTERFACE_COUNT = 5;

        /// <summary>Default XRange when connected</summary>
        private const XRanges DEFAULT_XRANGE = XRanges.PLUSMINUS_50;

        /// <summary>Default IP address used when connecting using Open method</summary>
        private const string DEFAULT_IP = "10.0.41.68";

        /// <summary>name of DLL library to load functions from</summary>
        private const string DRIVER_DLL_NAME = "Drivers\\ScannerMicroEpsilonSC2750.dll";

        /// <summary>Size of buffer for short texts, eg. name of device</summary>
        private const int BUFFER_SHORTSTRING_LENGTH = 300; //300 characters should be enough for the name of device

        /// <summary>possible return values</summary>
        public class ReturnValues
        {
            public const int DEVICE_NAME_NOT_SUPPORTED = 4;
            public const int PACKET_SIZE_CHANGED = 3;
            public const int CONTAINER_MODE_HEIGHT_CHANGED = 2;
            public const int OK = 1;
            public const int NOT_AVAILABLE = 0;
        }

        #endregion

        #region private data members

        /// <summary>Internal handle to connected device</summary>
        private uint hDevice = 0;

        /// <summary>internal connection flag</summary>
        private bool isConnected = false;

        /// <summary>actual set resolution</summary>
        private uint resolution = 0;

        /// <summary>stores model of actually connected device</summary>
        private ScannerType scannerType = 0;

        #endregion

        #region private methods

        /// <summary>Retrieves selected communication interface</summary>
        private InterfaceType GetInterfaceType()
        {
            return dllAPI.GetInterfaceType(hDevice);
        }

        /// <summary>Performs connection procedure via specified interface</summary>
        private bool ConnectionProcedure(InterfaceType Interface, uint parameter1, int parameter2)
        {
            int retValue = 0;

            if (Connected) //already connected
            {
                Debug.WriteLine("scanCONTROL: Already connected");
                return false;
            }

            if (hDevice == 0) //not created yet
            {
                hDevice = dllAPI.CreateLLTDevice(Interface); //create object
                if (hDevice == 0) //error during creating object
                {
                    Debug.WriteLine("scanCONTROL: Error during creating LLT object");
                    return false;
                }
            }

            retValue = dllAPI.SetDeviceInterface(hDevice, parameter1, parameter2); //set IP address
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when applying IP address/Node ID/COM number");
                return false;
            }

            retValue = dllAPI.Connect(hDevice); //connect to device
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when connecting");
                return false;
            }

            if (GetXRange() == 0) //equidistant span is neccessary for many functions, must be switched on
                SetXRange(DEFAULT_XRANGE);

            isConnected = true; //setting internal connection flag
            if (OnConnected != null)
                OnConnected(this, EventArgs.Empty); //raise event

            return true;
        }

        /// <summary>Gets all three properties stored in Threshold property</summary>
        private uint GetThreshAndFilters()
        {
            int retValue = 0;
            uint resValue = 0;

            retValue = dllAPI.GetFeature(hDevice, dllAPI.FEATURE_FUNCTION_THRESHOLD, ref resValue);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when reading Threshold property");
                return 0;
            }

            return resValue;
        }

        /// <summary>Retrieves if Ambient light supression is enabled</summary>
        private bool GetAmbientLight()
        {
            return (GetThreshAndFilters() & 0x400) > 0; //masking 10.bit which contains desired data
        }

        /// <summary>Enables/disables ambient light supression</summary>
        private void SetAmbientLight(bool enable)
        {
            int retValue = 0;
            uint dword = GetThreshAndFilters();

            if (enable)
                dword |= (1u << 10); //sets 10. bit
            else
                dword &= ~(1u << 10); //clears 10. bit

            retValue = dllAPI.SetFeature(hDevice, dllAPI.FEATURE_FUNCTION_THRESHOLD, dword);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when writing Ambient light supression property");
                return;
            }
        }

        /// <summary>Retrieves if Video filter is enabled</summary>
        private bool GetVideoFilter()
        {
            return (GetThreshAndFilters() & 0x800) > 0; //masking 11.bit which contains desired data
        }

        /// <summary>Enables/disables video filter</summary>
        private void SetVideoFilter(bool enable)
        {
            int retValue = 0;
            uint dword = GetThreshAndFilters();

            if (enable)
                dword |= (1u << 11); //sets 11. bit
            else
                dword &= ~(1u << 11); //clears 11. bit

            retValue = dllAPI.SetFeature(hDevice, dllAPI.FEATURE_FUNCTION_THRESHOLD, dword);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when writing Video filter property");
                return;
            }
        }

        /// <summary>Retrieves reflection threshold</summary>
        private uint GetThreshold()
        {
            return (GetThreshAndFilters() & 0x3FF); //masking 0..9bit which contains desired data
        }

        /// <summary>Sets value for reflection threshold</summary>
        private void SetThreshold(uint threshold)
        {
            int retValue = 0;
            uint dword = GetThreshAndFilters();

            dword &= ~(0x3FFu); //clears 0..9 bit
            dword |= (threshold & 0x3FF); //fill 0..9 bit with threshold data 

            retValue = dllAPI.SetFeature(hDevice, dllAPI.FEATURE_FUNCTION_THRESHOLD, dword);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when writing Threshold property");
                return;
            }
        }

        /// <summary>Retrieves if auto shutter is enabled</summary>
        private bool GetAutoShutter()
        {
            int retValue = 0;
            uint resValue = 0;

            retValue = dllAPI.GetFeature(hDevice, dllAPI.FEATURE_FUNCTION_SHUTTERTIME, ref resValue);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when reading Automatic shutter property");
                return false;
            }

            return (resValue & 0x1000000) > 0; //masking 24.bit which contains auto shutter setting
        }

        /// <summary>Enables/diables automatic shutter time regulation depending on measured object</summary>
        private void SetAutoShutter(bool value)
        {
            int retValue = 0;
            uint dword = ShutterTime;

            if (value)
                dword |= (1u << 24); //sets 24. bit
            else
                dword &= ~(1u << 24); //clears 24. bit

            retValue = dllAPI.SetFeature(hDevice, dllAPI.FEATURE_FUNCTION_SHUTTERTIME, dword);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when writing Automatic shutter property");
                return;
            }
        }

        /// <summary>Retrieves time when shutter is opened</summary>
        private uint GetShutterTime()
        {
            int retValue = 0;
            uint resValue = 0;

            retValue = dllAPI.GetFeature(hDevice, dllAPI.FEATURE_FUNCTION_SHUTTERTIME, ref resValue);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when reading Shutter time property");
                return 0;
            }

            return (resValue & 0xFFF); //masking 0..11bit which contains shuttertime
        }

        /// <summary>Sets shutter time</summary>
        private void SetShutterTime(uint value)
        {
            int retValue = 0;
            uint dword = value;

            if (AutoShutter)
                dword |= (1u << 24); //sets 24. bit
            else
                dword &= ~(1u << 24); //clears 24. bit

            retValue = dllAPI.SetFeature(hDevice, dllAPI.FEATURE_FUNCTION_SHUTTERTIME, dword);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when writing Shutter time property");
                return;
            }
        }

        /// <summary>Retrieves dead time when shutter is closed</summary>
        private uint GetIdleTime()
        {
            int retValue = 0;
            uint resValue = 0;

            retValue = dllAPI.GetFeature(hDevice, dllAPI.FEATURE_FUNCTION_IDLETIME, ref resValue);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when reading Idle time property");
                return 0;
            }

            return (resValue & 0xFFF); //masking 0..11bit which contains idletime
        }

        /// <summary>Sets idle time</summary>
        private void SetIdleTime(uint value)
        {
            int retValue = 0;

            retValue = dllAPI.SetFeature(hDevice, dllAPI.FEATURE_FUNCTION_IDLETIME, value);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when writing Idle time property");
                return;
            }
        }

        /// <summary>Gets actual laser power. Allowed modes are OFF, HALF and FULL.</summary>
        private LaserModes GetLaser()
        {
            int retValue = 0;
            uint resValue = 0;

            retValue = dllAPI.GetFeature(hDevice, dllAPI.FEATURE_FUNCTION_LASERPOWER, ref resValue);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when reading Laser power property");
                return 0;
            }

            return (LaserModes)resValue;
        }

        /// <summary>Sets laser power. Allowed modes are OFF, HALF and FULL.</summary>
        private void SetLaser(LaserModes mode)
        {
            int retValue = 0;

            retValue = dllAPI.SetFeature(hDevice, dllAPI.FEATURE_FUNCTION_LASERPOWER, (uint)mode);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when writing Laser power property");
                return;
            }
        }

        /// <summary>retrieves actually set resolution</summary>
        /// <returns>returns value of resolution</returns>
        private uint GetResolution()
        {
            if (resolution > 0) //resolution is stored in internal memory
                return resolution;

            // we dont know value of resolution -> acquire it from device
            int retValue = 0;
            uint resValue = 0;

            retValue = dllAPI.GetResolution(hDevice, ref resValue); //gets resolution
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when getting resolution");
                return 0;
            }

            resolution = resValue; //save resolution to internal memory
            return resValue;
        }

        /// <summary>sets desired resolution</summary>
        /// <returns>returns true if resolution was accepted</returns>
        private void SetResolution(uint value)
        {
            int retValue = 0;

            retValue = dllAPI.SetResolution(hDevice, value); //sets resolution
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when setting resolution");
                return;
            }

            resolution = value; //save to internal parameter
        }

        /// <summary>Returns model of actually connected scanner</summary>
        private ScannerType GetScannerType()
        {
            if (scannerType != 0) //scanner type is stored in internal memory
                return scannerType;

            // we dont know model of device -> acquire it from device
            int retValue = 0;
            ScannerType model = 0;

            retValue = dllAPI.GetLLTType(hDevice, ref model); //sets resolution
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when getting scanner type");
                return 0;
            }

            scannerType = model; //save model to internal memory
            return model;
        }

        /// <summary>Returns name of actually connected device</summary>
        private string GetDeviceName()
        {
            int retValue = 0;
            byte[] buffer = new byte[BUFFER_SHORTSTRING_LENGTH];

            retValue = dllAPI.GetDeviceName(hDevice, buffer, BUFFER_SHORTSTRING_LENGTH, null, 0);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when getting scanner name");
                return "";
            }

            return System.Text.Encoding.UTF8.GetString(buffer);
        }

        /// <summary>Writes dword into the filter property. Used by other filter functions</summary>
        private bool SetFilterProperty(uint dword)
        {
            int retValue = 0;

            retValue = dllAPI.SetFeature(hDevice, dllAPI.FEATURE_FUNCTION_PROFILE_FILTER, dword);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when writing Profile filter property");
                return false;
            }

            return true;
        }

        /// <summary>Reads dword from the filter property. Used by other filter functions</summary>
        private uint GetFilterProperty()
        {
            int retValue = 0;
            uint resValue = 0;

            retValue = dllAPI.GetFeature(hDevice, dllAPI.FEATURE_FUNCTION_PROFILE_FILTER, ref resValue);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when reading Profile filter property");
                return 0;
            }

            return resValue;

        }

        /// <summary>Reads dword from the filter property. Used by other filter functions</summary>
        private uint GetSerialNumber()
        {
            int retValue = 0;
            uint resValue = 0;

            retValue = dllAPI.GetFeature(hDevice, dllAPI.FEATURE_FUNCTION_SERIAL, ref resValue);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when reading Serial number");
                return 0;
            }

            return resValue;

        }

        /// <summary>Reads dword from the filter property. Used by other filter functions</summary>
        private uint[] GetLLTVersions()
        {
            int retValue = 0;
            uint DSP = 0;
            uint FPGA1 = 0;
            uint FPGA2 = 0;

            retValue = dllAPI.GetLLTVersions(hDevice, ref DSP, ref FPGA1, ref FPGA2);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when reading firmware version");
                return new uint[0];
            }

            return new uint[] { DSP, FPGA1, FPGA2 };
        }

        /// <summary>Enables/disables filtering of invalid points</summary>
        private void SetInvalidPointsFilter(bool enable)
        {
            uint dword = GetFilterProperty();

            if (enable)
                dword |= (1u << 11); //sets 11. bit
            else
                dword &= ~(1u << 11); //clears 11. bit

            SetFilterProperty(dword);
        }

        /// <summary>Retrieves if filtering of invalid points is enabled</summary>
        private bool GetInvalidPointsFilter()
        {
            uint dword = GetFilterProperty();
            return ((dword & (1u << 11)) == (1u << 11)); //returns value of 11. bit
        }

        /// <summary>Retrieves which range of X axis is set</summary>
        private XRanges GetXRange()
        {
            uint dword = GetFilterProperty();
            return (XRanges)((dword & (0x7 << 4)) >> 4); //returns value of 6..4 bit
        }

        /// <summary>Sets range of scanning in X axis</summary>
        private void SetXRange(XRanges range)
        {
            uint dword = GetFilterProperty();

            dword &= ~(7u << 4); //clears 4..6. bit
            dword |= ((uint)range << 4); //write to 4..6. bit

            SetFilterProperty(dword);
        }

        /// <summary>Retrieves settings of median filter</summary>
        private NumSamples GetMedianFilter()
        {
            uint dword = GetFilterProperty();
            return (NumSamples)((dword & (0x3 << 2)) >> 2); //returns value of 2..3 bit
        }

        /// <summary>Enables/Disables median filter</summary>
        private void SetMedianFilter(NumSamples samples)
        {
            uint dword = GetFilterProperty();

            dword &= ~(3u << 2); //clears 2..3. bit
            dword |= ((uint)samples << 2); //sets 2..3. bit

            SetFilterProperty(dword);
        }

        /// <summary>Retrieves settings of median filter</summary>
        private NumSamples GetAverageFilter()
        {
            uint dword = GetFilterProperty();
            return (NumSamples)(dword & 0x3); //returns value of 0..1 bit
        }

        /// <summary>Enables/Disables median filter</summary>
        private void SetAverageFilter(NumSamples samples)
        {
            uint dword = GetFilterProperty();

            dword &= ~(3u); //clears 0..1. bit
            dword |= (uint)samples; //sets 0..1. bit

            SetFilterProperty(dword);
        }

        /// <summary>Retrieves minimal time between two packets setting (in ms)</summary>
        private uint GetPacketDelay()
        {
            int retValue = 0;
            uint resValue = 0;

            retValue = dllAPI.GetFeature(hDevice, dllAPI.FEATURE_FUNCTION_PACKET_DELAY, ref resValue);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when reading Packet delay property");
                return 0;
            }

            return resValue;
        }

        /// <summary>Sets minimal time between two packets in ms. Neccessary when more devices connected.</summary>
        /// <param name="time">minimal span of two consequent packets in ms</param>
        private void SetPacketDelay(uint time)
        {
            int retValue = 0;

            retValue = dllAPI.SetFeature(hDevice, dllAPI.FEATURE_FUNCTION_PACKET_DELAY, time);
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when writing Packet delay property");
                return;
            }
        }

        #endregion

        #region constructor / destructor

        /// <summary>Constructor of ScannerLMS400 object</summary>
        public scanCONTROL2750()
        {

        }

        /// <summary>Destructor of ScannerLMS400 object</summary>
        public void Dispose()
        {
            if (Connected)
                Disconnect();

            dllAPI.DelDevice(hDevice);

            hDevice = 0;
        }

        #endregion

        #region public methods

        /// <summary>Connects to device using Ethernet</summary>
        /// <param name="IPaddress">IP address of device to connect</param>
        /// <returns>returns true if connected successfully</returns>
        public bool ConnectETH(string IPaddress)
        {
            //converting IP address to desired format
            string[] IPnums = IPaddress.Split('.');
            if (IPnums.Length != 4) //syntax error in IP address
            {
                Debug.WriteLine("scanCONTROL: Syntax error in IP address");
                return false;
            }
            uint IPpacked = (uint.Parse(IPnums[0]) << 24) | (uint.Parse(IPnums[1]) << 16) | (uint.Parse(IPnums[2]) << 8) | uint.Parse(IPnums[3]);

            return ConnectionProcedure(InterfaceType.INTF_TYPE_ETHERNET, IPpacked, 0);
        }

        /// <summary>Connects to device using Ethernet</summary>
        /// <param name="portNumber">number of serial port</param>
        /// <returns>returns true if connected successfully</returns>
        public bool ConnectSerial(uint portNumber)
        {
            return ConnectionProcedure(InterfaceType.INTF_TYPE_SERIAL, portNumber, 115200);
        }

        /// <summary>Connects to device using FireWire</summary>
        /// <param name="portNumber">Node ID</param>
        /// <returns>returns true if connected successfully</returns>
        public bool ConnectFirewire(uint nodeID)
        {
            return ConnectionProcedure(InterfaceType.INTF_TYPE_FIREWIRE, nodeID, 0);
        }

        /// <summary>Connects to device using Ethernet and default IP defined in constants</summary>
        public void Open()
        {
            ConnectETH(DEFAULT_IP);
        }
        
        /// <summary>Terminates connection with device</summary>
        /// <returns>returns true if disconnected successfully</returns>
        public bool Disconnect()
        {
            int retValue = 0;

            if (Measuring) //if is measuring -> stop
                StopMeas();

            if (!Connected) //already disconnected
            {
                Debug.WriteLine("scanCONTROL: Already disconnected");
                return false;
            }

            retValue = dllAPI.Disconnect(hDevice); //connect to device
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " during disconnect");
                return false;
            }

            isConnected = false; //setting internal connection flag
            if (OnClosed != null)
                OnClosed(this, EventArgs.Empty); //raise event

            return true;
        }
        public void Close()
        {
            Disconnect();
        }

        /// <summary>Starts continuous measuring process</summary>
        /// <returns>returns true if measuring started successfully</returns>
        public bool StartMeas()
        {
            int retValue = 0;

            retValue = dllAPI.TransferProfiles(hDevice, TransferProfileType.NORMAL_TRANSFER, 1); //enables (=1) continuous (=normal) measure
            //if (retValue != ReturnValues.OK)
            //{
            //    Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when starting continuous measure");
            //    return false;
            //}

            //Sleep for a while to warm up the transfer (required by device)
            System.Threading.Thread.Sleep(100);

            if (OnMeasureStart != null)
                OnMeasureStart(this, EventArgs.Empty); //raise event

            return true;
        }

        /// <summary>Stops continuous measuring process</summary>
        /// <returns>returns true if measuring stopped successfully</returns>
        public bool StopMeas()
        {
            int retValue = 0;

            retValue = dllAPI.TransferProfiles(hDevice, TransferProfileType.NORMAL_TRANSFER, 0); //disables (=0) continuous (=normal) measure
            if (retValue != ReturnValues.OK)
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when stopping continuous measure");
                return false;
            }

            if (OnMeasureStop != null)
                OnMeasureStop(this, EventArgs.Empty); //raise event

            return true;
        }

        /// <summary>Returns measured values (profile data)</summary>
        /// <returns>array of points defined by X and Z coordinates</returns>
        public XZpoint[] GetValues()
        {
            int retValue = 0;
            uint uiLostProfiles = 0;
            byte[] profileBuffer = new byte[Resolution * 16 + 16]; //allocation buffer for received data
            double[] valuesX = new double[Resolution]; //buffer for storing converted X values
            double[] valuesZ = new double[Resolution]; //buffer for storing converted Z values
            ushort[] valuesReflectionWidth = new ushort[Resolution]; //buffer for storing converted Z values
            ushort[] valuesMaxReflection = new ushort[Resolution]; //buffer for storing converted Z values

            //acquiring pure byte data
            retValue = dllAPI.GetActualProfile(hDevice, profileBuffer, (uint)profileBuffer.GetLength(0), ProfileConfig.QUARTER_PROFILE, ref uiLostProfiles);
            if (retValue != profileBuffer.GetLength(0))
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " when reading actual profile");
                if (retValue == -104) //no profiles, maybe it is disconnected
                {
                    if (dllAPI.IsTransferingProfiles(hDevice) == 0) //it is not transferring data -> it is disconnected
                    {
                        isConnected = false;
                        if (OnClosed != null)
                            OnClosed(this, EventArgs.Empty);
                    }
                }
                return null;
            }
            
            //conversion byte data to values
            retValue = dllAPI.ConvertProfile2Values(hDevice, profileBuffer, Resolution, ProfileConfig.QUARTER_PROFILE, DeviceType, 0, 1, valuesReflectionWidth, valuesMaxReflection, null, valuesX, valuesZ, null, null);
            if (((retValue & dllAPI.CONVERT_X) == 0) || ((retValue & dllAPI.CONVERT_Z) == 0) || ((retValue & dllAPI.CONVERT_MAXIMUM) == 0) || ((retValue & dllAPI.CONVERT_WIDTH) == 0))
            {
                Debug.WriteLine("scanCONTROL: Error " + retValue.ToString() + " during converting byte data to values");
                return null;
            }

            //reconfiguring data to XZPoint[]
            XZpoint[] points = new XZpoint[Resolution];
            for (int i = 0; i < Resolution; i++)
            {
                points[i] = new XZpoint(valuesX[i], valuesZ[i], valuesMaxReflection[i], valuesReflectionWidth[i]);
            }

            return points;
        }

        /// <summary>Returns true if driver is actually connected to device</summary>
        public bool IsAlive()
        {
            return Connected;
        }

        #endregion

        #region public parameters

        /// <summary>Selected interface for communication</summary>
        public InterfaceType Interface { get { return GetInterfaceType(); } }

        /// <summary>Format of data</summary>
        public uint Resolution { get { return GetResolution(); } set { SetResolution(value); } }

        /// <summary>Minimal time between two packets in us [0-1000 allowed]. Neccessary when more devices connected.</summary>
        public uint PacketDelay { get { return GetPacketDelay(); } set { SetPacketDelay(value); } }

        /// <summary>Type (version) of connected device</summary>
        public ScannerType DeviceType { get { return GetScannerType(); } }

        /// <summary>Name, type and version of connected device</summary>
        public string DeviceName { get { return GetDeviceName(); } }
        public string Name { get { return GetDeviceName(); } }

        /// <summary>Returs array of DSP, FPGA1 and FPGA2 firmware versions</summary>
        public uint[] FirmwareVersions { get { return GetLLTVersions(); } }

        /// <summary>Returs array of DSP, FPGA1 and FPGA2 firmware versions</summary>
        public uint SerialNumber { get { return GetSerialNumber(); } }

        /// <summary>Defines time when shutter is on. Defined in number of 10us, allowed range 1 .. 4095 (eg. 10..40950us)</summary>
        public uint ShutterTime { get { return GetShutterTime(); } set { SetShutterTime(value); } }

        /// <summary>Defines time when shutter is off. Defined in number of 10us, allowed range 1 .. 4095 (eg. 10..40950us)</summary>
        public uint IdleTime { get { return GetIdleTime(); } set { SetIdleTime(value); } }

        /// <summary>Enables automatic regulation of shutter time depending on scanned object</summary>
        public bool AutoShutter { get { return GetAutoShutter(); } set { SetAutoShutter(value); } }

        /// <summary>Enables supression of ambient light</summary>
        public bool AmbientLightSupression { get { return GetAmbientLight(); } set { SetAmbientLight(value); } }

        /// <summary>Enables video filter</summary>
        public bool VideoFilter { get { return GetVideoFilter(); } set { SetVideoFilter(value); } }

        /// <summary>Threshold for the selection of reflections. At targets with several reflections this may lead to better results.</summary>
        public uint ReflectionThreshold { get { return GetThreshold(); } set { SetThreshold(value); } }

        /// <summary>Returns true if driver is actually connected to device</summary>
        public bool Connected { get { return isConnected; } }

        /// <summary>Returns true if device is actually measuring. Some functions are disabled then.</summary>
        public bool Measuring { get { return (dllAPI.IsTransferingProfiles(hDevice) != 0); } }

        /// <summary>Enables interpolating of invalid points</summary>
        public bool InvalidPointsFilter { get { return GetInvalidPointsFilter(); } set { SetInvalidPointsFilter(value); } }

        /// <summary>Mode of Laser emitter</summary>
        public LaserModes LaserPower { get { return GetLaser(); } set { SetLaser(value); } }

        /// <summary>Sets range of scanning in X axis</summary>
        public XRanges XRange { get { return GetXRange(); } set { SetXRange(value); } }

        /// <summary>Enables/disables median filter</summary>
        public NumSamples MedianFilter { get { return GetMedianFilter(); } set { SetMedianFilter(value); } }

        /// <summary>Enables/disables average filter</summary>
        public NumSamples AverageFilter { get { return GetAverageFilter(); } set { SetAverageFilter(value); } }

        /// <summary>Returns actual configuration of device packed in one object</summary>
        public object Params { get { return new SensorInformation(this); } }

        #endregion

        #region static methods

        /// <summary>returns array of actually connected devices by selected interface. There is an array of IP addresses (when Ethernet selected) or Node IDs (Firewire) or usable COM ports (Serial)</summary>
        /// <returns></returns>
        public static string[] GetAvailableDevices(InterfaceType interf)
        {
            uint[] devices = new uint[MAX_INTERFACE_COUNT];

            uint handle = dllAPI.CreateLLTDevice(interf); //create instance for selected interface
            if (handle == 0)
            {
                Debug.WriteLine("scanCONTROL: Error when scanning for connected devices (cannot create device)");
                return new string[0];
            }

            int numOfDevices = dllAPI.GetDeviceInterfaces(handle, devices, devices.GetLength(0)); //acquire list of devices
            if (numOfDevices < 0)
            {
                Debug.WriteLine("scanCONTROL: Error " + numOfDevices.ToString() + " when scanning for connected devices");
                return new string[0];
            }

            dllAPI.DelDevice(handle); //delete instance

            if (interf == InterfaceType.INTF_TYPE_ETHERNET) // if Ethernet, parse IP address
            {
                string[] strDevices = new string[numOfDevices];
                for (int i = 0; i < numOfDevices; i++)
                {
                    strDevices[i] = (devices[i] >> 24).ToString() + "." +
                                    ((devices[i] >> 16) & 0xFF).ToString() + "." +
                                    ((devices[i] >> 8) & 0xFF).ToString() + "." +
                                    (devices[i] & 0xFF).ToString();
                }
                return strDevices;
            }
            else //otherwise, pass the numbers (Node ID or Com port)
                return Array.ConvertAll<uint, string>(devices, intParameter => intParameter.ToString());

        }

        #endregion

        #region events

        /// <summary>Connected to device</summary>
        public event EventHandler OnConnected;

        /// <summary>Disconnected from device</summary>
        public event EventHandler OnClosed;

        /// <summary>Continuous measure start</summary>
        public event EventHandler OnMeasureStart;

        /// <summary>Continuous measure stop</summary>
        public event EventHandler OnMeasureStop;

        #endregion
    }

}

