Class FSUIPC

java.lang.Object
com.mouseviator.fsuipc.FSUIPC

public class FSUIPC
extends java.lang.Object

General description

This is another FSUIPC wrapper class... This one uses FSUIPCWrapper to perform stuff with FSUIPC, but sort of hides the low-level approach required by FSUIPCWrapper and hides it behind more object oriented Java approach - or at least, I tried to achieve that...

The FSUIPCWrapper uses native methods and therefore, needs a dll library to be loaded for it to work. But since the JVM can be 32bit, or 64bit, the library implementing the native functions must also be 32bit or 64bit. The FSUIPCWrapper will not load any library by itself. Therefore, it is up the programmer to do it before using any (actually some, but rather stick to all) functions from this (and therefore, the FSUIPCWrapper class) class. But you don't have to write them yourself, you can use load() function (which will try to detect platform by itself), or either one of load32() and load64() if you have your own mechanism on detecting platform, such as JNI.

This class is build upon a concept of data requests. Data requests simply are data that you want to read/write to/from the simulator via FSUIPC. Each data request is associated with the offset (what data you want to read/write, you know that from other programming with FSUIPC), has its type - READ or WRITE. For data request to be supported by this class, it must implement the IDataRequest interface. You can also use the DataRequest class as a foundation for building new data request object, or use any predefined ones from the com.mouseviator.fsuipc.datarequest.primitives package (which defines data request for all primitive data types supported by FSUIPC, such as byte, integer, string etc.).

Gathering data to/from the simulator via FSUIPC's inter process communication is time consuming, that is why it would be bad to do it each time we need i single information, like airspeed update. This class has two arrays (queues) for storing data requests. One is for one-time requests - the data, that you need to read/write once, since they do not change often, such as situation file. You can add requests to this queue using the addOneTimeRequest(com.mouseviator.fsuipc.datarequest.IDataRequest) function. Add multiple requests and then process them all via single FSUIPC inter process communication call using the processRequestsOnce() function. Note that if the processing is successful, the one-time queue will be emptied (you will need to add requests again for another data exchange).

The second type of queue is the one for continual data requests. For data, that you need to read/write continuously, such as airspeed. You can add requests to this queue using the addContinualRequest(com.mouseviator.fsuipc.datarequest.IDataRequest) function. The process them all using the processRequests(int, boolean) function. This function will start a thread that will trigger the FSUIPC inter process communication periodically in the interval specified when calling the function, and all registered data requests will be updated every time. If there are any request in the one-time queue, they will be processed to, so there is no need to call processRequestsOnce() after starting the processing thread using the processRequests(int, boolean) function. To stop the processing thread, call the cancelRequestsProcessing() function.

Well, I know it is nice that the processing thread updates the data, but how would you know that it actually happened? There is an event listener for this. Implement your own IFSUIPCListener and register it using the addListener(com.mouseviator.fsuipc.IFSUIPCListener) function. Then, FSUIPC class will call IFSUIPCListener.onProcess(java.util.AbstractQueue) function after each FSUIPC inter process communication requests are done (in another words, after each FSUIPC process() function call). See also another callback functions of the IFSUIPCListener interface. I think they are pretty self-explanatory

Usage example

This is basic example on using this class. First of all, we need to get an instance of it.


  FSUIPC fsuipc = FSUIPC.getInstance();
 

Than we need to load the native library. In the code below, we do that using the load() function, which will try to determine whether to load 32 bit, or 64 bit library automatically. But you can also use load32() or load64() to load specific version if you have your own logic for determining JVM platform. When the library is loaded, we can connect to FSUIPC.


   byte result = FSUIPC.load();
   if (result != FSUIPC.LIB_LOAD_RESULT_OK) {
       System.out.println("Failed to load native library. Quiting...");
       return;
   }

   int ret = fsuipc.connect(FSUIPCWrapper.FSUIPCSimVersion.SIM_ANY);
   if (ret == 0) {
       System.out.println("Flight sim not found");
   } else {
       System.out.println("Flight Sim found!");
   }
 

Now, we can create some data request to read/write some data to/from simulator. In the code below, we create data request to read airspeed and register it for one time processing.


  //Helper for gathering aircraft data
  AircraftHelper aircraftHelper = new AircraftHelper();

  //Get IAS data request and register it for time processing
  FloatRequest ias = (FloatRequest) fsuipc.addOneTimeRequest(aircraftHelper.getIAS());

  //Let FSUIPC process all one-time requests
  int ret = fsuipc.processRequestsOnce();

  //Later on, print the requested data
  if (ret == FSUIPC.PROCESS_RESULT_OK) {
      System.out.println("Aircraft IAS: " + String.valueOf(ias.getValue()));
  }
 

Note that in the code above, we are using the helper method AircraftHelper.getIAS() to get the IAS data request. That helper provides modified request object, that will return the IAS in Kts as float value, even though that FSUIPC itself will return it as integer value * 128. The modified data request IDataRequest.getValue() will perform the calculation for us. The code above is the same as this:


  //Get IAS data request and register it for time processing
  IntRequest ias = new IntRequest(0x02BC);
  fsuipc.addOneTimeRequest(ias);

  //Let FSUIPC process all one-time requests
  int ret = fsuipc.processRequestsOnce();

  //Later on, print the requested data
  if (ret == FSUIPC.PROCESS_RESULT_OK) {
      System.out.println("Aircraft IAS (method 2): " + String.valueOf(ias.getValue() / 128.0f));
  }
 

If we want to monitor the airspeed continuously, we would register it with addContinualRequest(com.mouseviator.fsuipc.datarequest.IDataRequest) rather than with addOneTimeRequest(com.mouseviator.fsuipc.datarequest.IDataRequest). In this case, it would also be nice to define the listener so that we know when processing happened. Note that the example uses logger variable, which is for logging messages (if you don't know about logging in Java, just ignore it) - kind of instead of System.out.println... Also notice, that inside the IFSUIPCListener.onProcess(java.util.AbstractQueue) callback we use SwingUtilities.invokeLater. This is because the processing happens in FSUIPC class processing thread - NOT EDT thread... and all GUI updates should happen on EDT thread...that is how to do it. The last line of code in this example, that cancels the requests processing, is not really needed. The FSUIPC class check the last result code after each to call to FSUIPC's processing function and it will cancel the processing thread once it finds out that FSUIPC connection to sim has been lost. But you can of course call it if you want to stop the processing for any reason.


 //Helper for gathering aircraft data
 AircraftHelper aircraftHelper = new AircraftHelper();

 //Get IAS data request and register it for time processing
 FloatRequest ias = (FloatRequest) fsuipc.addContinualRequest(aircraftHelper.getIAS());

 //We will get results from continual request processing using the listener
 IFSUIPCListener fsuipcListener = new IFSUIPCListener() {
       @Override
       public void onConnected() {
          logger.info("FSUIPC connected!");
       }

       @Override
       public void onDisconnected() {
          logger.info("FSUIPC disconnected!");
       }

       @Override
       public void onProcess(AbstractQueue<IDataRequest> arRequests) {
          System.out.println("FSUIPC continual request processing callback!");

          //GUI updates on EDT thread
          SwingUtilities.invokeLater(new Runnable() {
              @Override
              public void run() {
                  lblIAS.setText(String.format("%d Kts", (int) Math.ceil(ias.getValue())));
              }
          }
       }

       @Override
       public void onFail(int lastResult) {
           logger.log(Level.INFO, "Last FSUIPC function call ended with error code: {0}, message: {1}",
                 new Object[]{lastResult,
                    FSUIPC.FSUIPC_ERROR_MESSAGES.get(FSUIPCWrapper.FSUIPCResult.get(lastResult))});
       }
 }

 //Add our listener to FSUIPC
 fsuipc.addListener(fsuipcListener);

 //Start continual processing, every 250 miliseconds
 fsuipc.processRequests(250, true);

 .
 .
 .
 .

 //Later, stop processing
 fsuipc.cancelRequestsProcessing();
 

When we are finished, we should disconnect the FSUIPC (this will also release used resources).


  fsuipc.disconnect();
 
Author:
Mouseviator
  • Field Details

  • Constructor Details

    • FSUIPC

      public FSUIPC()
  • Method Details

    • getInstance

      public static FSUIPC getInstance()
      Returns:
      Instance of FSUIPC.
    • load

      public static byte load()
      This function will try to load the 32bit/64bit native library that implements this wrapper native methods. Note that it tries to determine the architecture by reading the "sun.arch.data.model" system property and then call load32() or load64() based on result. The result can also be "unknown", by that case non of the functions will be called. Note that this is not bulletproof, if you ae using JNI or similar API, use it and call 32/64 bit load function by yourself.
      Returns:
      Will return LIB_LOAD_RESULT_OK if the library is successfully loaded, LIB_LOAD_RESULT_FAILED if the loading of the library fails and LIB_LOAD_RESULT_ALREADY_LOADED if the library is already loaded (either 32 or 64 bit one)
    • load32

      public static byte load32()
      This function will try to load 32bit version of native library that implements this wrapper native methods.
      Returns:
      Will return LIB_LOAD_RESULT_OK if the library is successfully loaded, LIB_LOAD_RESULT_FAILED if the loading of the library fails and LIB_LOAD_RESULT_ALREADY_LOADED if the library is already loaded (either 32 or 64 bit one)
    • load64

      public static byte load64()
      This function will try to load 64bit version of native library that implements this wrapper native methods.
      Returns:
      Will return LIB_LOAD_RESULT_OK if the library is successfully loaded, LIB_LOAD_RESULT_FAILED if the loading of the library fails and LIB_LOAD_RESULT_ALREADY_LOADED if the library is already loaded (either 32 or 64 bit one)
    • addListener

      public boolean addListener​(IFSUIPCListener listener)
      This will add listener. If the listener is already present, it will not be added.
      Parameters:
      listener - A listener to add.
      Returns:
      True if listener was added, false otherwise (you passed null, or what Collection.add(java.lang.Object) returns.).
    • removeListener

      public boolean removeListener​(IFSUIPCListener listener)
      This will remove registered listener.
      Parameters:
      listener - A listener to remove.
      Returns:
      True if listener was removed, false otherwise (you passed null, or what Collection.remove(java.lang.Object) returns.).
    • removeAllListeners

      public void removeAllListeners()
      This will remove all listeners
    • connect

      public int connect​(FSUIPCWrapper.FSUIPCSimVersion simVersion)
      Open FSUIPC connection to selected simulator version.
      Parameters:
      simVersion - A simulator to connect to.
      Returns:
      0 if not connected, 1 (or non-zero) when successfully connected.
    • isConnected

      public boolean isConnected()
      Will return the value of the internal AtomicBoolean variable that is being updated when FSUIPC is connected/disconnected.
      Returns:
      True if FSUIPC is connected, false otherwise.
    • waitForConnection

      public boolean waitForConnection​(FSUIPCWrapper.FSUIPCSimVersion simVersion, int repeatPeriod)
      This method will init thread that will continuously try to connect to simulator via FSUIPC till success. WARNING: This functions will first try to stop any currently running waiting thread. This might take some time - thus may block the current thread! Should not be called from main EDT thread.
      Parameters:
      simVersion - A simulator version to which to connect.
      repeatPeriod - A time in seconds to repeat the connection attempts.
      Returns:
      True if thread is successfully started, false otherwise.
    • cancelRequestsProcessing

      public boolean cancelRequestsProcessing()
      This function will cancel the thread that is running the continual request processing. WARNING: This functions will try to stop any currently running processing thread. This might take some time - thus may block the current thread! Should not be called from main EDT thread.
      Returns:
      True if thread was canceled, false if there was some problem (exception).
    • disconnect

      public void disconnect()
      Closes connection to FSUIPC.
    • getLastResult

      public FSUIPCWrapper.FSUIPCResult getLastResult() throws java.security.InvalidParameterException
      Return the last result from last FSUIPC operation. This should be one from FSUIPCWrapper.FSUIPCResult
      Returns:
      FSUIPCWrapper.FSUIPCResult of last FSUIPC function call.
      Throws:
      java.security.InvalidParameterException - if FSUIPCWrapper.getResult() return value not supported by FSUIPCWrapper.FSUIPCResult. That would mean this library is outdated!
    • getLastErrorMessage

      public java.lang.String getLastErrorMessage() throws java.security.InvalidParameterException
      This function will return a message from FSUIPC_ERROR_MESSAGES for the last FSUIPC function call. It will call getLastResult() internally to get the last result code.
      Returns:
      A string error message for the last FSUIPC function call.
      Throws:
      java.security.InvalidParameterException - if getLastResult() throws the same Exception.
    • getFSVersion

      public java.lang.String getFSVersion()
      This function will return one text from FSUIPC_SIM_VERSION_TEXT based on the result of the call of the function FSUIPCWrapper.getFSVersion(). If it fails to convert result of function FSUIPCWrapper.getFSVersion() to FSUIPCWrapper.FSUIPCSimVersion, the returned value will be an empty string.
      Returns:
      String value.
    • getFSVersion

      public java.lang.String getFSVersion​(FSUIPCWrapper.FSUIPCSimVersion simVersion)
      This function will return one text from FSUIPC_SIM_VERSION_TEXT based on the result of the call of the function FSUIPCWrapper.getFSVersion().If it fails to convert result of function FSUIPCWrapper.getFSVersion() to FSUIPCWrapper.FSUIPCSimVersion, the returned value will be an empty string.
      Parameters:
      simVersion - A sim version to get string representation for.
      Returns:
      String value.
    • getVersion

      public java.lang.String getVersion()
      Returns string representation of FSUIPC version. Calls FSUIPCWrapper.getVersion() to get FSUIPC version as number first.
      Returns:
      String representation of FSUIPC version.
    • getLibVersion

      public java.lang.String getLibVersion()
      Returns string representation of FSUIPC library version. Calls FSUIPCWrapper.getVersion() to get FSUIPC version as number first. Note that this version number is hard-coded in FSUIPC C lib and was not updated for years :)
      Returns:
      String representation of FSUIPC library version.
    • addOneTimeRequest

      public IDataRequest addOneTimeRequest​(IDataRequest dataRequest)
      This method will add data request to the one-time requests array.This array will be emptied once successfully processed via the processRequestsOnce() function.
      Parameters:
      dataRequest - Read or Write data request.
      Returns:
      The passed dataRequest if not null, otherwise null.
    • addContinualRequest

      public IDataRequest addContinualRequest​(IDataRequest dataRequest)
      This method will add data request to the continual requests array.
      Parameters:
      dataRequest - Read or Write data request.
      Returns:
      The passed dataRequest if not null, otherwise null.
    • removeContinualRequest

      public boolean removeContinualRequest​(IDataRequest dataRequest)
      This function will remove data request from continual requests array.
      Parameters:
      dataRequest - A data request to remove.
      Returns:
      True if continual requests array changed (the request was removed).
    • clearContinualRequests

      public boolean clearContinualRequests()
      This will clear an array of continual requests and stop continual request processing thread.
      Returns:
      The result of call to cancelRequestsProcessing(), which is done before the requests array is cleared.
    • getOneTimeRequests

      public java.util.AbstractQueue<IDataRequest> getOneTimeRequests()
      This function returns an array of one time data requests.
      Returns:
      The queue of one-time requests.
    • processRequestsOnce

      public int processRequestsOnce()
      This function will process all stored one-time data requests. To store one time data request to the queue, use addContinualRequest(com.mouseviator.fsuipc.datarequest.IDataRequest) method.
      Returns:
      This function will return PROCESS_RESULT_OK if everything went Ok. It will return PROCESS_RESULT_REQUESTS_EMPTY if the one-time requests array was empty before calling this function - ie. nothing to process. The PROCESS_RESULT_REQUESTS_STORE_FAILED will be returned if it failed to store any of the request to FSUIPC (FSUIPCWrapper.read(int, int, byte[]) and FSUIPCWrapper.write(int, int, byte[]) is being called in the background based on request type). And lastly, the PROCESS_RESULT_REQUESTS_PROCESS_FAILED if the call to FSUIPCWrapper.process() returned error. In the case of last two scenarios, use FSUIPCWrapper.getResult() to find out what did not work.
    • processRequests

      public int processRequests​(int repeatPeriod, boolean cancelRunning)
      This function will start thread for continual processing of FSUIPC data requests. Specify time period in milliseconds of how often to process requests. This will process all requests, either the ones stored via the addOneTimeRequest(com.mouseviator.fsuipc.datarequest.IDataRequest) or addContinualRequest(com.mouseviator.fsuipc.datarequest.IDataRequest) functions. If processing is completed without errors, the "one time" requests array will be cleared. WARNING: This function will try to stop any currently running processing thread (if cancelRunning is true). This might take some time - thus may block the current thread! Should not be called from main EDT thread.
      Parameters:
      repeatPeriod - How often to process the requests. Milliseconds.
      cancelRunning - Whether to cancel the task if currently running and start a new one.
      Returns:
      This function will return PROCESS_RESULT_OK if thread is successfully started. It will return PROCESS_RESULT_FAILED_TO_CANCEL_THREAD if the thread is already running, the cancelRunning parameter was true and it failed to cancel the currently running thread. If the thread is already running and the cancelRunning parameter is false, PROCESS_RESULT_THREAD_ALREADY_RUNNING will be returned. The PROCESS_RESULT_FAILTED_TO_START_THREAD will be returned if it fails to start new thread.
    • getLastProcessingTime

      public long getLastProcessingTime()
      Returns the time in nanoseconds that the last call to FSUIPC process function took. It will be updated by processRequests(int, boolean) and processRequestsOnce() functions.
      Returns:
      Time in milliseconds.