Jump to content
The simFlight Network Forums

fsuipc client vs lua


FSEPIC
 Share

Recommended Posts

Hi Paul , 

i am looking to use either fsuipc client dll , or lua 

and i noticed in one of your replies , that you suggested to a user to try lua instead of the client dll 

Is there any functional difference , as both talk to fsuipc ?

I would like to hear your take on this , ?  (   to me,  on the surface lua looks very simple to program ,  and no GBs VS download etc needed 😉  ) 

best regards , 

Peter

Link to comment
Share on other sites

Hi Peter,

The two technologies are used for different kinds of tasks, so It really depends on what your program will do.

Generally Lua can be thought of as a way of writing 'plug ins' that extend the functionality of FSUIPC. Typical uses are

  • driving hardware connected to the sim
  • monitoring the flight, looking for certain conditions then taking actions (e.g. play a cabin announcement at the right time)
  • creating functions that can be bound to buttons/keys (e.g. set fuel to 80% when a key is pressed)
  • creating simple display windows to show information from the sim. (e.g. calculate time until fuel runs out and show it in a window in real time).

Using the DLL in an external program is better (or required) if any of these are true:

  • you need a complex user interface where users need to interact with your application (selecting menus, filling out forms etc, drawing charts)
  • the application needs to do other things beside interacting with FSUIPC (e.g. has a flight planner than plans routes)
  • the application will require hundreds of lines of code (debugging and code organisation is much easier in Visual Studio)

Other considerations:

  • Lua scripts can only be run on a registered copy of FSUIPC. An external program can be used with an unregistered copy. If you want others to use your program this might be a consideration.
  • If you need to access many L:Vars (Panel Variables) Lua is much more efficient for this.

Hopefully that helps. If you're still unsure let me know what your program will be doing and I can give more specific advice.

Paul

Link to comment
Share on other sites

Hi Paul, 

- thanks for your very clear explanation ,  ( looks like your code 😉  ) 

- the task at hand is to let my arduino board talk to fsx   , the buttons and switches , and also receive  data , to set leds , and  7-segm. display ,   therefore no user interaction is necessary .  - so the lua program would be only an intermediary ,  and it can speak "serial "  very easily according to the lua docs . 

-  could you expand a bit on the öther considerations ?  Why is lua mor effiecint ?

-  i thought from your docs that the .net client also needs a copy of fsuipc installed ?

Peter  

Link to comment
Share on other sites

Quote

Why is lua mor effiecint ?

It's just the way LVARs are read and written from an external program. The FSUIPC external interface can only process one LVar at a time. So each LVar requires a complete data exchange cycle (Process() Call). This takes time so reading 20 or more Lvars in real time isn't very practical from an external program. This doesn't apply to normal Offsets of course, just LVars.

Quote

-  i thought from your docs that the .net client also needs a copy of fsuipc installed ?

Yes, both require FSUIPC. Lua needs a paid (registered) copy. External programs can use a free (unregistered) copy.

Quote

the task at hand is to let my arduino board talk to fsx 

This seems like the sort of thing the Lua system was intended for, but I can't say how easy it would be; I'm not a Lua expert and don't do anything with hardware.

If you have a registered copy of FSUIPC I would try Lua first and see if you can get a switch and a LED working.

Paul

Link to comment
Share on other sites

  • 2 months later...

Hi Paul , 

- am still comparing , so last but not least ....  your dll ,  so diving into C# and Visual Studio .  It is a lot...  ;-) 

- this morning i got this error when trying to add the fsuipc dll as you described in your video .

Error        Could not install package 'FSUIPCClientDLL 3.1.20'. You are trying to install this package into a project that targets '.NETFramework,Version=v3.5', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author.

what gives ???

best  regards , 

 

Peter

 

 

 

Link to comment
Share on other sites

Hi Peter,

The minimum version of the .NET framework that my DLL will work with is 4.0. Your project seems to be targeting version 3.5 which is from the days of Windows XP, so very old. 

You'll need to change the target runtime by going to PROJECT -> Properties:

image.png.f6f03724bde6a2cf65237f2e511750de.png

The minimum I recommend is 4.5 which will run on any Windows from Vista onwards.

Paul

Link to comment
Share on other sites

Hi Paul , 

- am still comparing , so last but not least ....  your dll ,  so diving into C# and Visual Studio .  It is a lot...  😉

- this morning i got this error when trying to add the fsuipc dll as you described in your video .

Error        Could not install package 'FSUIPCClientDLL 3.1.20'. You are trying to install this package into a project that targets '.NETFramework,Version=v3.5', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author.

what gives ???

best  regards , 

 

Peter

 

Hi Paul , 

- thanks ,  that was indeed part of the problem , i took a gamble and added the fsuipcclient folder  to the solution , and  added then the reference to the dll .

- great , thanks ,  making progress now  

best regards , 

 

Peter

 

Link to comment
Share on other sites

  • 2 weeks later...

Hi Paul , 

- basically all is working now for pmdg , and receiving data over serial port  from hw,  starting to like Visual Studio ( just kidding 😉

- i  am using the FSUIPCConnection.SendControlToFS(PMDG_737_NGX_Control.    properties , which is fantastic  , thanks !   

- but it looks like  about 20 times about the same code , for the pushbutton switches .  for example :

---------------------------------------

//  event or loop , i can choose in my code  ,  so i am basically receiving the  pin numbers over a serial port .

case pin.EVT_MCP_LVL_CHG_SWITCH:

 FSUIPCConnection.SendControlToFS(PMDG_737_NGX_Control.EVT_MCP_LVL_CHG_SWITCH, Convert.ToInt32(!(MCP_annunLVL_CHG.Value > 0)) );
break;
 

---------------------------------------------------

pin.EVT_MCP_LVL_CHG_SWITCH:   defined as  23   ( in separate class ,  similar to enumeration ) using your naming conventions

PMDG_737_NGX_Control.EVT_MCP_LVL_CHG_SWITCH  defined as  70023

Convert.ToInt32(!(MCP_annunLVL_CHG.Value > 0))  , using petes naming convention , and your > 0 trick 😉 

 

 

Q .    I would like to keep this  idea , but use a dictionary , would that be more elegant  / short ???

- So use the 23 as key , lookup your value .  Then use the same key in a second dictionary , lookup the last MCP_annunLVL_CHG.Value  name , and  its value ( maybe in tuple) .

-  in this way ,  it would be 1 function call , with the key as parameter .

- the  key value pairs or tuples could be neatly stored in a text file ,  and read into the dictionary at formload time . 

best regards , Peter

Edit: 

I thought about it again , and don't know if it is possible in c# ,  

If the text file has rows like  this:  ( for the above example ) , ( to be read into the dictionary )

Pinnumber( enum) (dict-key)                               controlname                                                                                                 offsetname 

pin.EVT_MCP_LVL_CHG_SWITCH                      PMDG_737_NGX_Control.EVT_MCP_LVL_CHG_SWITCH                     MCP_annunLVL_CHG    

then call function  , and use the 2 strings in this  row  from the dict.

to BUILD the code line

 FSUIPCConnection.SendControlToFS("controlname" , " offsetname" .Value > 0)) );

if this magic is possible , then that would be the most simple solution , unless you have other tricks up your sleeve ...

best regards , Peter

 

 

 

 

 

 

 

 

 

 

 

 

 

Edited by FSEPIC
clarification
Link to comment
Share on other sites

Hi Paul , 

that was the idea , indeed.  

For the implementation ( not for the dict. stuff )  , do you have some pointers ,  what this is called in c#  ??  So i can study this  in the net .  I found something called " dynamic programming "  , but that was about recursive programming , and not applicable here , i think ...    ,

 

best regards , Peter 

Link to comment
Share on other sites

Hi Peter,

It's quite complicated to compile and run .NET code at runtime. It's usually called Dynamic Execution.

I don't think it's needed here however; there is a way you can do this with the base (untyped) Offset class. I suggest something along these lines... 


Text File:

In the text file have the following:

pin, controlName, offset address, type, size

e.g. 

23, EVT_MCP_LVL_CHG_SWITCH, FFFF, byte, 1


Dictionary:

Create a structure to hold the pin, the event, the offset, and a method to get the offset value back as an int:

    internal struct pinInfo
    {
        public int pin;
        public string eventName;
        public Offset offset;
        public string offsetType;

        public pinInfo(int Pin, string EventName, Offset FSUIPCOffset, string OffsetType)
        {
            this.pin = Pin;
            this.eventName = EventName;
            this.offset = FSUIPCOffset;
            this.offsetType = OffsetType;
        }

        public int getOffsetValue()
        {
            int value = 0;
            switch (this.offsetType)
            {
                case "byte":
                    value = (int)this.offset.GetValue<byte>();
                    break;
                case "short":
                    value = (int)this.offset.GetValue<short>();
                    break;
                case "int":
                    value = this.offset.GetValue<int>();
                    break;
            }
            return value;
        }
    }

Create the dictionary to hold these entries, keyed on the pin:

Dictionary<int, pinInfo> pins = new Dictionary<int, pinInfo>();


Populating the dictionary:

As you read each line in the text file, create an instance of pinInfo and add to the dictionary:

            // these would be from the file...
            string pinID = "23";
            string controlName = "EVT_MCP_LVL_CHG_SWITCH";
            string offsetAddressHex = "FFFF";
            string offsetType = "byte";
            string offsetSize = "1";

            // convert pin to int
            int pinInt = int.Parse(pinID);
            // convert offset address from string to int (as hexadecimal)
            int offsetAddressInt = int.Parse(offsetAddressHex, NumberStyles.HexNumber);
            // convert size to int
            int offsetSizeInt = int.Parse(offsetSize);
            // convert event string to int
            int eventInt = (int)Enum.Parse(typeof(PMDG_737_NGX_Control), controlName, false);

            // create (untyped) offset
            Offset offset = new Offset(offsetAddressInt, offsetSizeInt);

            // create new pinInfo and add to the dictionary
            pinInfo newPin = new pinInfo(pinInt, eventInt, offset, offsetType);
            pins.Add(newPin.pin, newPin);


Using the dictionary:

When the pin number arrives, get the pinInfo using the pin number. Then use the pin info to get the offset value and send the control:         

            int incommingPinNumber = 23;

            pinInfo pin = pins[incommingPinNumber];
            FSUIPCConnection.SendControlToFS(pin.controlID, pin.getOffsetValue());

Paul

Link to comment
Share on other sites

  • 2 weeks later...

Hi Paul , 

was going to ask you about the possibility of events ( for data coming from fsuipc ) ,  to avoid "flooding" the serial port 😉    Then I came across your   FSUIPC WebSocket Server project .    That would nicely solve the problem:   Studying the protocol ,  because it is used on a LAN only , the   latency is about 0 .   

edit :   could not connect , because did not notice the protocol parameter in the websocketsharp lib .  , All is fine now , great thanks Paul !

 

Best regards  ,

 

 

 

 

Peter

 

 

 

Link to comment
Share on other sites

Hi Peter,

I don't know either of those libraries. You can specify the protocol on the native WebSocket object built into .NET. I guess using that would require more coding than using a library though.

However, using the WebSocket Server just for event notifications seems like a lot of work.

Using the DLL you can check the .ValueChanged property on the Offset. This will tell you if the value is different since the last Process() call. If this is false you can choose not to send the data to the serial port. This feature was only added in the last few months.

Do you think that will work for you?

Paul 

Link to comment
Share on other sites

Hi Paul, 

- right now , ( after implementing the "pininfodictionary "  ,  as per your example above  )  i decided to to the same thing on the receiving end , and created a "leddictionary ". 

- so with 2 lines of code , i could get rid of all offset definitions in c# :

 var list = LedDictionary.Keys.ToList();
                foreach (var key in list)
                    checkbox[LedDictionary[key].LedNumber].Checked = LedDictionary[key].getOffsetValue() > 0;

( i am using checkboxes , (  i found a way to dynamically generate them ) ,  as a way to "fake"    Events .  )  So the whole program is now reduced from hundreds of lines of code to  < 100 lines of code  .   

-  you are right , is more work , but on the other hand , since i  use 2 .csv files,    they can be read in in 1 codeline using a lib  ( by now you understand i am a fan of using libs 😉

- and i am starting to like c# VS ( forget LUA ....  )   .

- so i would like to give it a try .    

- now on testing i did run into a snag

- websocket is open , and  i am testing on pmdg SE  , 737 , FSX Win10

- i am sending the command as per your example in your doc:

                test cmdtest = new test
                {
                    Command = "about.read",
                    Name = "about",
                };

                string json = JsonConvert.SerializeObject(cmdtest);
 

===========================================

this is the response:

ws open
{"Command":"about.read","Name":"about"}
fsuipc says: {
  "command": "Unknown",
  "name": "Unknown",
  "success": false,
  "errorMessage": "Request message is invalid. No command specified.",
  "errorCode": "BadMessage"
}

 

any ideas ?   it would be very nice if there was also 1 example in c# in the doc  ,   for us who don't want to delve to deep into jscript  😉   ,    

 

best regards , 

 

Peter

 

 

 

   

 

 

 

Link to comment
Share on other sites

Hi Peter,

It's good you got the connection working.

The problem with your request is that the property names are case-sensitive. Your 'Command' should be 'command'. Like this:

               test cmdtest = new test
                {
                    command = "about.read",
                    name = "about",
                };
Quote

 it would be very nice if there was also 1 example in c# in the doc  ,   for us who don't want to delve to deep into jscript

I'll put it on the list of things to do. I really didn't expect anyone to be using it from C# :-)

Paul

Link to comment
Share on other sites

 

- great ,  works like a charm now .

- Well ,  is a nice challenge ...  ,  e.g.

name: 'altitude', address: 0x0570, type: 'int', size: 8 }, , apparently in JS you can format JSON like that.

but the JSON docs state hex numbers are not supported  😉 ,  so  i guess will have to use string here .

ah , going through the doc examples , i did get exception , maybe quitting FS before the server  ??? 

and small data error in HDG , consistently 2 degrees off, but no big deals .

best regards , Peter 

 

- right now , ( after implementing the "pininfodictionary "  ,  as per your example above  )  i decided to to the same thing on the receiving end , and created a "leddictionary ". 

- so with 2 lines of code , i could get rid of all offset definitions in c# :

 var list = LedDictionary.Keys.ToList();
                foreach (var key in list)
                    checkbox[LedDictionary[key].LedNumber].Checked = LedDictionary[key].getOffsetValue() > 0;

( i am using checkboxes , (  i found a way to dynamically generate them ) ,  as a way to "fake"    Events .  )  So the whole program is now reduced from hundreds of lines of code to  < 100 lines of code  .   

-  you are right , is more work , but on the other hand , since i  use 2 .csv files,    they can be read in in 1 codeline using a lib  ( by now you understand i am a fan of using libs 😉

- and i am starting to like c# VS ( forget LUA ....  )   .

- so i would like to give it a try .    

- now on testing i did run into a snag

- websocket is open , and  i am testing on pmdg SE  , 737 , FSX Win10

- i am sending the command as per your example in your doc:

 

 

ws open
{"Command":"about.read","Name":"about"}
fsuipc says: {
  "command": "Unknown",
  "name": "Unknown",
  "success": false,
  "errorMessage": "Request message is invalid. No command specified.",
  "errorCode": "BadMessage"
}

 

 

 

 

 

   

 

 

 

Link to comment
Share on other sites

Quote

but the JSON docs state hex numbers are not supported  😉 ,  so  i guess will have to use string here .

I don't think a string will work here. It's got to be a number when it arrives at the server.

JSON may not support hex, but C# does! It should be fine. You declare it as a normal hex number in the C# object. Then your JSON encoder will write it out as a base 10 integer and send it like that. My server will understand that.

Quote

and small data error in HDG , consistently 2 degrees off, but no big deals .

Sounds like a magnetic variation error. The headings from FSUIPC are usually in degrees True. You need to convert to Magnetic (used by the aircraft instruments) using the current Magnetic Variation. There's an offset for that.

Paul

Link to comment
Share on other sites

  • 2 weeks later...

Hi Paul , 

you wrote: "-Using the DLL you can check the .ValueChanged property on the Offset" -   ,   yes ,  great , thanks! , works nicely.

( the alternative implementation using the websockets is indeed lot of work  ;-)  ,  but nice challenge .   It is a bit more hairy in c# , because  the websockets data arrive in separate thread, so invoke must be used .  In your JS examples that problem does not seem to arise .  )

-      Using your .net client dll, all is working stable , but  the encoders are a bit sluggish , to my taste .   I am experimenting with settings of 10ms  for timerMain ,  but  i can't see a noticeable improvement.   ( they are in their own group of 6 ) .    I did do separate testing of the encoders  ,  with a serial port viewer and there is no problem there  with speed.  That seems to suggest that the  bottleneck is the part where the fsuipc offset  is written . 

Is there a limit / update rate  as far as fsuipc is concerned ?

best regards , 

Peter

Link to comment
Share on other sites

Quote

Is there a limit / update rate  as far as fsuipc is concerned ?

The limit will be how long it takes for a Process() call to complete.

This depends on your particular setup. e.g. if the flight sim is running complex 3rd-party aircraft, if the PC is doing other things, if there are any other addons using FSUIPC or SimConnect etc.

On my PC with no addons and a default Cessna it takes about 12ms. So a timer loop under 12ms would not improve the update rate. 12 ms would be the maximum.

On your system you can see the average time taken for Process() calls by displaying the value of this property:

FSUIPCConnection.Statistics.ProcessAvg.TimeForProcessCall

Let you application run for a bit so it can build an average. The value is in milliseconds.

You can also see min and max values using ProcessMin and ProcessMax.

Paul

 

Link to comment
Share on other sites

Not to step on this discussion, having a similar problem.
This is an untested hypothesis based on a post in the main FSUIPC room, of which I cannot find.
It will get rid of the constant reading / writing of the aircraft's internal vars therefore speeding up the response.
It should also work on almost all encoders when there is a matching led display being used ( Hand to eye response ). Some things like frequencies may be a bit tougher.
-----------------------------------------------------------------------
Pete had mentioned something in regards to encoder delays and I think it is the way to go, if this is how he does it.. I believe it is.
This is an example for a MCP heading encoder w/ LED display -

1) When the aircraft is first loaded, get the aircraft's internal HDG value and send it to a user offset. Do this only once, as an ini. ( .exe, .dll or .lua )

2a) From the encoder, set it up in the FSUIPC interface as button/key press command. ( the key/button polling setting in the .ini is your adjustment )

2b) Use the special commands listed on/near page 38 of FSUIPC for Advanced Users.pdf, "FSUIPC Offset Controls:"  
Heading up:
x5200zzzz Offset Word Cyclic Increment (user offset = zzzz), hexadecimal, with parameter(s) of:
    Slow Increment ( 1° ), with a cyclic limit of 359 - 00000001011001110000000000000001 = 0x1670001 = 23527425
    Fast Increment ( 10° ), with a cyclic limit of 359 - 00000001011001110000000000001010 = 0x167000A = 23527434
Decrement should be similar with the opposite control. 

2c) Use the offset directly, with formatting as needed, to drive the LED display, I believe this will be quicker in response, for the eyes.

3) Using .exe, .dll or .lua, listen for the user offset to change, if it does, only then update the aircraft's internal heading variable so it matches the led display ( the offset ). 

Again, just a hypothesis.. Good luck, carry on. 😁

Roman
  
 

Link to comment
Share on other sites

  • 4 weeks later...

Hi Paul, 

( btw , thanks Roman ,  for your input , 2b has always worked very well for me , but is not the solution, 3 needs sync of hw and fsuipc ..... so...     ;-)   ...  )

 

Solved , i did not call the function:    connection()  ;-)    ,    deleted too much of your code...

 

as easy as it was to get your websockets  example to work in c#  ,  the harder it seems in javascript ,  so no copy paste ?

The setup  using javascript only  as simple as possible ,  because JS is rather new to me

index.html and  connection.js live in  1 folder  ,   i started   from  the JS example in Visual Studio and Visual Studio Code.

here is the index.html

===============

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>FSUIPC WebSocket Server</title>
    <script type="text/javascript" src="connection.js"></script>
</head>
<body>
    <h2>FSUIPC WebSocket</h2>
</body>
</html>

-----------------

and here the connection.js

var connection = new function () {
    var ws = null// WebSocke
    this.start = function () {
    };
    this.stop = function () {
        if (ws) {
            ws.close();
        }
    };
    var clearMessages = function () {
        document.getElementById("connectionStatus").innerText = "";
        document.getElementById("connectionError").innerText = "";
    };
    this.openConnection = function () {
        if (!ws) {
 
            clearMessages();
 
            // Create a new WebSocket. 
            // The server url is from the text box on the form
            //var serverURL = document.getElementById("serverURL").value;
 
            ws = new WebSocket("ws://localhost:2048/fsuipc/""fsuipc");
           
            // Handle the onopen event
            ws.onopen = function () {
                console.log("open");
                document.getElementById("connectionStatus").innerText = "Connection Open";
            };
 
            // Handle the onclose event
            ws.onclose = function () {
                document.getElementById("connectionStatus").innerText = "Connection Closed";
                // clear the WebSocket so we can try again
                ws = null;
            };
 
            // Handle the onerror event
            ws.onerror = function () {
                console.log("error");
 
                document.getElementById("connectionError").innerText = "WebSocket Error";
            };
        }
    }
 
    this.closeConnection = function () {
        if (ws) {
            clearMessages();
            ws.close();
 
        }
    }
 
};
 
------------------------
 
only this line changed :  
    ws = new WebSocket("ws://localhost:2048/fsuipc/""fsuipc");
 
in the chrome dev tools i get this result :
messages: 
connected 
 
and under headers:
    1. Request URL:
      ws://127.0.0.1:5500/index.html/ws
    2. Request Method:
      GET
    3. Status Code:
      101 Switching Protocols
  1. Response Headersview source
    1. Connection:
      Upgrade
    2. Sec-WebSocket-Accept:
      ulRY6vxyLAMR+6zT9PFntYFrNiY=
    3. Upgrade:
      websocket
  2. Request Headersview source
    1. Accept-Encoding:
      gzip, deflate, br
    2.  
      Accept-Language:
      en-US,en;q=0.9
    3. Cache-Control:
      no-cache
    4. Connection:
      Upgrade
    5. Host:
      127.0.0.1:5500
    6. Origin:
      http://127.0.0.1:5500
    7. Pragma:
      no-cache
    8. Sec-WebSocket-Extensions:
      permessage-deflate; client_max_window_bits
    9. Sec-WebSocket-Key:
      1Ns9YcRWpb+SRrzeS0jt+A==
    10. Sec-WebSocket-Version:
      13
    11. Upgrade:
      websocket
    12. User-Agent:
      Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.193 Safari/537.36
 
==================
 
 
But the fsuipc websockets server  has :  number of sockets connected  0 
 
( i start the index.html from the live server in VS code )   :     http://127.0.0.1:5500/index.html 
 
What could be the problem ???
 
Peter
 
 
 
 
 
 
 
 

 

    

Link to comment
Share on other sites

I think there are two problems here...

1. There is nothing in the script (connection.js) or the webpage (index.html) that runs any of the code in the connection function. I therefore don't think this is being run at all.

2. The dev tools output says the WebSocket URL is ws://127.0.0.1:5500/index.html/ws. However your code (which I don't think is being run) says ws://localhost:2048/fsuipc/. I can't see anywhere in your code that the first URL is referenced, and it's certainly wrong. It shouldn't have index.html in it.

I suspect problem 2 is because you've got the WebSocket server running on the same port as your WWW server (5500). The WebSocket server should run on a different port. Your code specifies 2048 so you should configure the WebSocket server to run on 2048. 

For problem 1 you need to call connection.openConnection() from somewhere. Maybe a button press on the webpage. Maybe when the page has finished loading (set code for the window.onload event).

Note that you'll need a <div> on the html page with an ID of 'connectionStatus' to show the connection status.

Paul

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use. Guidelines Privacy Policy We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.