'
' Quantis Wrapper for VB.NET
'
' Copyright (c) 2004-2010 id Quantique SA, Carouge/Geneva, Switzerland
' All rights reserved.
'
' ----------------------------------------------------------------------------
'
' Redistribution and use in source and binary forms, with or without
' modification, are permitted provided that the following conditions
' are met:
' 1. Redistributions of source code must retain the above copyright
'    notice, this list of conditions, and the following disclaimer,
'    without modification.
' 2. Redistributions in binary form must reproduce the above copyright
'    notice, this list of conditions and the following disclaimer in the
'    documentation and/or other materials provided with the distribution.
' 3. The name of the author may not be used to endorse or promote products
'    derived from this software without specific prior written permission.
'
' THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
' ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
' IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
' ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
' ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
' DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
' OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
' HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
' LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY.
'
' ----------------------------------------------------------------------------
'
' Alternatively, this software may be distributed under the terms of the
' terms of the GNU General Public License version 2 as published by the
' Free Software Foundation.
'
' This program is distributed in the hope that it will be useful, but
' WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
' or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
' for more details.
'
' You should have received a copy of the GNU General Public License
' along with this program; if not, write to the Free Software
' Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
'

Imports System
Imports System.Collections.Generic
Imports System.Runtime.InteropServices
Imports System.Text


Namespace idQ

  ''' <summary>
  '''  Define constants of Quantis devices types
  ''' </summary>
  Enum QuantisDeviceType
    ''' <summary>Quantis PCI or PCI-Express</summary>
    QUANTIS_DEVICE_PCI = 1

    ''' <summary>Quantis USB</summary>
    QUANTIS_DEVICE_USB = 2
  End Enum


  ''' <summary>
  ''' Quantis exception
  ''' </summary>
  Public Class QuantisException
    Inherits ApplicationException
    ''' <summary>
    ''' Initializes a new instance of the QuantisException class.
    ''' </summary>
    Public Sub New()
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the System.ApplicationException class with a 
    ''' specified error message.
    ''' </summary>
    ''' <param name="message">A message that describes the error.</param>
    Public Sub New(ByVal message As String)
      MyBase.New(message)
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the System.ApplicationException class with a 
    ''' specified error message and a reference to the inner exception that is the 
    ''' cause of this exception.
    ''' </summary>
    ''' <param name="message">A message that describes the error.</param>
    ''' <param name="inner">
    ''' The exception that is the cause of the current exception. If the innerException 
    ''' parameter is not a null reference, the current exception is raised in a catch 
    ''' block that handles the inner exception.
    ''' </param>
    Public Sub New(ByVal message As String, ByVal inner As Exception)
      MyBase.New(message, inner)
    End Sub
  End Class


  ''' <summary>
  ''' Provides methods for configuring Quantis device and read random data.
  ''' </summary>
  Class Quantis


    Private deviceType As QuantisDeviceType
    Private deviceNumber As UInteger

    ''' <summary>
    ''' Checks if result of a function in the Quantis Library is as error. 
    ''' In such a case a QuantisException is thrown
    ''' </summary>
    ''' <param name="result">The result of the function.</param>
    ''' <exception cref="QuantisException">Quantis was unable to perform the operation.</exception>
    Private Shared Sub CheckError(ByVal result As Integer)
      ' All errors are negative
      If (result < 0) Then
        Throw New QuantisException(QuantisStrError(result))
      End If
    End Sub


    ''' <summary>
    ''' Initializes a new instance of the Quantis class to access the specified device.
    ''' </summary>
    ''' <param name="deviceType">The type of Quantis device.</param>
    ''' <param name="deviceNumber">The Quantis device number. Note that first device is 0.</param>
    Public Sub New(ByVal deviceType As QuantisDeviceType, ByVal deviceNumber As UInteger)
      Me.deviceType = deviceType
      Me.deviceNumber = deviceNumber
    End Sub


    ''' <summary>
    ''' Resets the Quantis board.
    ''' </summary>
    ''' <remarks>
    ''' This function do not generally has to be called, since the board is automatically reset.
    ''' </remarks>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Sub BoardReset()
      CheckError(QuantisBoardReset(deviceType, deviceNumber))
    End Sub


    ''' <summary>
    ''' Returns the number of specific Quantis type devices that have been detected on the system.
    ''' </summary>
    ''' <param name="deviceType">The type of Quantis device.</param>
    ''' <returns>The number of Quantis devices that have been detected on the system.</returns>
    ''' <remarks>If an error occured while counting the devices, 0 is returned.</remarks>
    Public Shared Function Count(ByVal deviceType As QuantisDeviceType) As Integer
      Return QuantisCount(deviceType)
    End Function


    ''' <summary>
    ''' Get the version of the board.
    ''' </summary>
    ''' <returns>The version of the board.</returns>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Function GetBoardVersion() As Integer
      Dim result As Integer = QuantisGetBoardVersion(deviceType, deviceNumber)
      CheckError(result)
      Return result
    End Function


    ''' <summary>
    ''' Returns the version of the driver as a decimal number composed by the 
    ''' major and minor number: <code>version = major.minor</code>.
    ''' </summary>
    ''' <returns>The version of the board.</returns>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Function GetDriverVersion() As Single
      Dim result As Single = QuantisGetDriverVersion(deviceType)
      CheckError(CInt(result))
      Return result
    End Function


    ''' <summary>
    ''' Returns the library version as a number composed by the major and minor
    ''' number: <code>version = major.minor</code>.
    ''' </summary>
    ''' <returns>The library version.</returns>
    Public Shared Function GetLibVersion() As Single
      Return QuantisGetLibVersion()
    End Function


    ''' <summary>
    ''' Returns the number of modules that have been detected on a Quantis device.
    ''' </summary>
    ''' <returns>the number of detected modules.</returns>
    ''' <seealso>GetModulesMask</seealso>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Function GetModulesCount() As Integer
      Dim result As Integer = QuantisGetModulesCount(deviceType, deviceNumber)
      CheckError(result)
      Return result
    End Function


    ''' <summary>
    ''' Returns the data rate (in Bytes per second) provided by the Quantis device.
    ''' </summary>
    ''' <returns>the data rate provided by the Quantis device.</returns>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Function GetModulesDataRate() As Integer
      Dim result As Integer = QuantisGetModulesDataRate(deviceType, deviceNumber)
      CheckError(result)
      Return result
    End Function


    ''' <summary>
    ''' Returns a bitmask of the modules that have been detected on a Quantis
    ''' device, where bit <em>n</em> is set if module <em>n</em> is present.
    ''' For instance when 5 (1101 in binary) is returned, it means that modules
    ''' 0, 2 and 3 have been detected.
    ''' </summary>
    ''' <returns>
    ''' A bitmask of the detected modules or a negative value when an error occured.
    ''' </returns>
    ''' <seealso>GetModulesStatus</seealso>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Function GetModulesMask() As Integer
      Dim result As Integer = QuantisGetModulesMask(deviceType, deviceNumber)
      CheckError(result)
      Return result
    End Function


    ''' <summary>
    ''' Get the power status of the modules.
    ''' </summary>
    ''' <returns>
    ''' TRUE if the modules are powered, FALSE if the modules are not powered and
    ''' a negative value when an error occured.
    ''' </returns>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Function GetModulesPower() As Boolean
      Dim result As Integer = QuantisGetModulesPower(deviceType, deviceNumber)
      CheckError(result)
      Return (result <> 0)
    End Function


    ''' <summary>
    ''' Returns the status of the modules on the device as a bitmask as defined 
    ''' in QuantisGetModulesMask. Bit <em>n</em> is set (equal to 1) only when
    ''' module <em>n</em> is enabled and functional. 
    ''' </summary>
    ''' <returns>A bitmask with the status of the modules.</returns>
    ''' <seealso>QuantisGetModulesMask</seealso>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Function GetModulesStatus() As Integer
      Dim result As Integer = QuantisGetModulesStatus(deviceType, deviceNumber)
      CheckError(result)
      Return result
    End Function


    ''' <summary>
    ''' Get a String with the serial number of the Quantis device.
    ''' </summary>
    ''' <returns>The serial number of the Quantis device or "S/N not available"
    ''' when an error occured or when the device do not support the operation 
    ''' (currently only Quantis USB returns a serial number).
    ''' </returns>
    Public Function GetSerialNumber() As String
      Return QuantisGetSerialNumber(deviceType, deviceNumber)
    End Function


    ''' <summary>
    ''' Disable one or more modules.
    ''' </summary>
    ''' <param name="modulesMask">
    ''' A bitmask of the modules (as specified in QuantisGetModulesMask) that 
    ''' must be disabled.
    ''' </param>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Sub ModulesDisable(ByVal modulesMask As Integer)
      CheckError(QuantisModulesDisable(deviceType, deviceNumber, modulesMask))
    End Sub


    ''' <summary>
    ''' Enable one or more modules.
    ''' </summary>
    ''' <param name="modulesMask">
    ''' A bitmask of the modules (as specified in QuantisGetModulesMask) that 
    ''' must be enabled.
    ''' </param>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Sub ModulesEnable(ByVal modulesMask As Integer)
      CheckError(QuantisModulesEnable(deviceType, deviceNumber, modulesMask))
    End Sub


    ''' <summary>
    ''' Reset one or more modules.
    ''' </summary>
    ''' <param name="modulesMask">
    ''' A bitmask of the modules (as specified in QuantisGetModulesMask) that 
    ''' must be reset.
    ''' </param>
    ''' <remarks>
    ''' This function just call QuantisModulesDisable and then QuantisModulesEnable 
    ''' with the provided modulesMask.
    ''' </remarks>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Sub ModulesReset(ByVal modulesMask As Integer)
      CheckError(QuantisModulesReset(deviceType, deviceNumber, modulesMask))
    End Sub


    ''' <summary>
    ''' Reads random data from the Quantis device.
    ''' </summary>
    ''' <param name="size">
    ''' The number of bytes to read. This value cannot be larger than 
    ''' QUANTIS_MAX_READ_SIZE (defined in Quantis.h)
    ''' </param>
    ''' <returns>A Byte array with random data.</returns>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Function Read(ByVal size As UInteger) As Byte()
      Dim bufferSize As New IntPtr(CInt(size))
      Dim buffer As Byte() = New Byte(size) {}
      Dim result As Integer = QuantisRead(deviceType, deviceNumber, buffer, bufferSize)
      CheckError(result)
      If result <> CInt(size) Then
        Throw New QuantisException("Read " + result + " bytes instead of " + size)
      End If

      If (buffer.Length = size) Then
        Return buffer
      ElseIf (buffer.Length = (size + 1)) Then
        If (buffer(buffer.Length - 1) = 0) Then
          ' Removes the ending null character
          Array.Resize(buffer, size)
          Return buffer
        End If
      End If

      ' Error
      Throw New QuantisException("Received " + buffer.Length + " bytes instead of " + size)
    End Function


    ''' <summary>
    ''' Reads a random double-precision floating-point number from the 
    ''' Quantis device.
    ''' </summary>
    ''' <returns>A random double-precision floating-point number between 0 and 1.</returns>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Function ReadDouble() As Double
      Dim data As Double
      CheckError(QuantisReadDouble_01(deviceType, deviceNumber, data))
      Return data
    End Function


    ''' <summary>
    ''' Reads a random double-precision floating-point number from the 
    ''' Quantis device.
    ''' </summary>
    ''' <param name="min">The minimal value the random number can take.</param>
    ''' <param name="max">The maximal value the random number can take.</param>
    ''' <returns>A random double-precision floating-point number.</returns>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Function ReadDouble(ByVal min As Double, ByVal max As Double) As Double
      Dim data As Double
      CheckError(QuantisReadScaledDouble(deviceType, deviceNumber, data, min, max))
      Return data
    End Function


    ''' <summary>
    ''' Reads a random single-precision floating-point number from the 
    ''' Quantis device.
    ''' </summary>
    ''' <returns>A random single-precision floating-point number.</returns>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Function ReadSingle() As Single
      Dim data As Single
      CheckError(QuantisReadFloat_01(deviceType, deviceNumber, data))
      Return data
    End Function


    ''' <summary>
    ''' Reads a random single-precision floating-point number from the 
    ''' Quantis device.
    ''' </summary>
    ''' <param name="min">The minimal value the random number can take.</param>
    ''' <param name="max">The maximal value the random number can take.</param>
    ''' <returns>A random single-precision floating-point number.</returns>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Function ReadSingle(ByVal min As Single, ByVal max As Single) As Single
      Dim data As Single
      CheckError(QuantisReadScaledFloat(deviceType, deviceNumber, data, min, max))
      Return data
    End Function


    ''' <summary>
    ''' Reads a random 32-bit signed integer from the Quantis device.
    ''' </summary>
    ''' <returns>A random double-precision floating-point number.</returns>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Function ReadInt() As Integer
      Dim data As Integer
      CheckError(QuantisReadInt(deviceType, deviceNumber, data))
      Return data
    End Function



    ''' <summary>
    ''' Reads a random 32-bit signed integer from the Quantis device.
    ''' </summary>
    ''' <param name="min">The minimal value the random number can take.</param>
    ''' <param name="max">The maximal value the random number can take.</param>
    ''' <returns>A random double-precision floating-point number.</returns>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Function ReadInt(ByVal min As Integer, ByVal max As Integer) As Integer
      Dim data As Integer
      CheckError(QuantisReadScaledInt(deviceType, deviceNumber, data, min, max))
      Return data
    End Function


    ''' <summary>
    ''' Reads a random 16-bit signed integer from the Quantis device.
    ''' </summary>
    ''' <returns>A random double-precision floating-point number.</returns>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Function ReadShort() As Short
      Dim data As Short
      CheckError(QuantisReadShort(deviceType, deviceNumber, data))
      Return data
    End Function



    ''' <summary>
    ''' Reads a random 16-bit signed integer from the Quantis device.
    ''' </summary>
    ''' <param name="min">The minimal value the random number can take.</param>
    ''' <param name="max">The maximal value the random number can take.</param>
    ''' <returns>A random double-precision floating-point number.</returns>
    ''' <exception cref="QuantisException">
    ''' Quantis was unable to perform the operation.
    ''' </exception>
    Public Function ReadShort(ByVal min As Short, ByVal max As Short) As Short
      Dim data As Short
      CheckError(QuantisReadScaledShort(deviceType, deviceNumber, data, min, max))
      Return data
    End Function

    ' ************************ Quantis.dll imports ************************

    <DllImport("quantis.dll", EntryPoint:="QuantisBoardReset")> _
    Private Shared Function QuantisBoardReset(ByVal deviceType As QuantisDeviceType, _
                                              ByVal deviceNumber As UInteger) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisCount")> _
    Private Shared Function QuantisCount(ByVal deviceType As QuantisDeviceType) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisGetBoardVersion")> _
    Private Shared Function QuantisGetBoardVersion(ByVal deviceType As QuantisDeviceType, _
                                                   ByVal deviceNumber As UInteger) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisGetDriverVersion")> _
    Private Shared Function QuantisGetDriverVersion(ByVal deviceType As QuantisDeviceType) As Single
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisGetLibVersion")> _
    Private Shared Function QuantisGetLibVersion() As Single
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisGetModulesCount")> _
    Private Shared Function QuantisGetModulesCount(ByVal deviceType As QuantisDeviceType, _
                                                   ByVal deviceNumber As UInteger) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisGetModulesDataRate")> _
    Private Shared Function QuantisGetModulesDataRate(ByVal deviceType As QuantisDeviceType, _
                                                      ByVal deviceNumber As UInteger) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisGetModulesMask")> _
    Private Shared Function QuantisGetModulesMask(ByVal deviceType As QuantisDeviceType, _
                                                  ByVal deviceNumber As UInteger) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisGetModulesPower")> _
    Private Shared Function QuantisGetModulesPower(ByVal deviceType As QuantisDeviceType, _
                                                   ByVal deviceNumber As UInteger) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisGetModulesStatus")> _
    Private Shared Function QuantisGetModulesStatus(ByVal deviceType As QuantisDeviceType, _
                                                    ByVal deviceNumber As UInteger) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisGetSerialNumber")> _
    Private Shared Function QuantisGetSerialNumber(ByVal deviceType As QuantisDeviceType, _
                                                   ByVal deviceNumber As UInteger) As String
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisModulesDisable")> _
    Private Shared Function QuantisModulesDisable(ByVal deviceType As QuantisDeviceType, _
                                                  ByVal deviceNumber As UInteger, _
                                                  ByVal modulesMask As Integer) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisModulesEnable")> _
    Private Shared Function QuantisModulesEnable(ByVal deviceType As QuantisDeviceType, _
                                                 ByVal deviceNumber As UInteger, _
                                                 ByVal modulesMask As Integer) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisModulesReset")> _
    Private Shared Function QuantisModulesReset(ByVal deviceType As QuantisDeviceType, _
                                                ByVal deviceNumber As UInteger, _
                                                ByVal modulesMask As Integer) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisRead", CharSet:=CharSet.Ansi)> _
    Private Shared Function QuantisRead(ByVal deviceType As QuantisDeviceType, _
                                        ByVal deviceNumber As UInteger, _
                                        ByVal data As Byte(), _
                                        ByVal size As IntPtr) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisReadDouble_01")> _
    Private Shared Function QuantisReadDouble_01(ByVal deviceType As QuantisDeviceType, _
                                              ByVal deviceNumber As UInteger, _
                                              ByRef data As Double) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisReadScaledDouble")> _
    Private Shared Function QuantisReadScaledDouble(ByVal deviceType As QuantisDeviceType, _
                                                    ByVal deviceNumber As UInteger, _
                                                    ByRef data As Double, _
                                                    ByVal min As Double, _
                                                    ByVal max As Double) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisReadFloat_01")> _
    Private Shared Function QuantisReadFloat_01(ByVal deviceType As QuantisDeviceType, _
                                                ByVal deviceNumber As UInteger, _
                                                ByRef data As Single) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisReadScaledFloat")> _
    Private Shared Function QuantisReadScaledFloat(ByVal deviceType As QuantisDeviceType, _
                                                   ByVal deviceNumber As UInteger, _
                                                   ByRef data As Single, _
                                                   ByVal min As Single, _
                                                   ByVal max As Single) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisReadInt")> _
    Private Shared Function QuantisReadInt(ByVal deviceType As QuantisDeviceType, _
                                           ByVal deviceNumber As UInteger, _
                                           ByRef data As Integer) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisReadScaledInt")> _
    Private Shared Function QuantisReadScaledInt(ByVal deviceType As QuantisDeviceType, _
                                                 ByVal deviceNumber As UInteger, _
                                                 ByRef data As Integer, _
                                                 ByVal min As Integer, _
                                                 ByVal max As Integer) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisReadShort")> _
    Private Shared Function QuantisReadShort(ByVal deviceType As QuantisDeviceType, _
                                             ByVal deviceNumber As UInteger, _
                                             ByRef data As Short) As Integer
    End Function


    <DllImport("quantis.dll", EntryPoint:="QuantisReadScaledShort")> _
    Private Shared Function QuantisReadScaledShort(ByVal deviceType As QuantisDeviceType, _
                                                   ByVal deviceNumber As UInteger, _
                                                   ByRef data As Short, _
                                                   ByVal min As Short, _
                                                   ByVal max As Short) As Integer
    End Function

    <DllImport("quantis.dll", EntryPoint:="QuantisStrError")> _
    Private Shared Function QuantisStrError(ByVal errorNumber As Integer) As String
    End Function

  End Class
End Namespace
