IP Comm Problems

dthomas32308dthomas32308 Independent DeveloperPosts: 41
hey all,

I'm probably being blind and just need someone elses eyes to wake me up again but I'm having issues communicating to a QSYS Core and keep getting errors. Heres the code so far.
PROGRAM_NAME='QSys'
(***********************************************************)
(***********************************************************)
(*  FILE_LAST_MODIFIED_ON: 04/05/2006  AT: 09:00:25        *)
(***********************************************************)
(* System Type : NetLinx                                   *)
(***********************************************************)
(* REV HISTORY:                                            *)
(***********************************************************)
(*
    $History: $
*)
(***********************************************************)
(*          DEVICE NUMBER DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_DEVICE

dvQsys_Core        =    0:3:0        //QSC Q-Sys Core
dvTP            =    10001:1:0    //AMX MVP8400


(***********************************************************)
(*               CONSTANT DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_CONSTANT

//Qsys
Qsys_IP_Address = '192.168.254.6'
Qsys_Port = 1702


#INCLUDE 'SNAPI'

(***********************************************************)
(*              DATA TYPE DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_TYPE

(***********************************************************)
(*               VARIABLE DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_VARIABLE

INTEGER flQsys_Status

(***********************************************************)
(*               LATCHING DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_LATCHING

(***********************************************************)
(*       MUTUALLY EXCLUSIVE DEFINITIONS GO BELOW           *)
(***********************************************************)
DEFINE_MUTUALLY_EXCLUSIVE

(***********************************************************)
(*        SUBROUTINE/FUNCTION DEFINITIONS GO BELOW         *)
(***********************************************************)
(* EXAMPLE: DEFINE_FUNCTION <RETURN_TYPE> <NAME> (<PARAMETERS>) *)
(* EXAMPLE: DEFINE_CALL '<NAME>' (<PARAMETERS>) *)

(***********************************************************)
(*                STARTUP CODE GOES BELOW                  *)
(***********************************************************)
DEFINE_START

(***********************************************************)
(*                THE EVENTS GO BELOW                      *)
(***********************************************************)
DEFINE_EVENT


DATA_EVENT[0]
{
    ONLINE:
    {
    IP_CLIENT_OPEN (dvQsys_Core.PORT, Qsys_IP_Address, Qsys_Port, IP_TCP)
    }
}

DATA_EVENT[dvQsys_Core]
{
    ONERROR:
    {
    IP_CLIENT_OPEN (dvQsys_Core.PORT, Qsys_IP_Address, Qsys_Port, IP_TCP)
    }
    ONLINE:
    {
    ON[dvQsys_Core, DEVICE_COMMUNICATING]
    }
    OFFLINE:
    {
    OFF[dvQsys_Core, DEVICE_COMMUNICATING]
    }
    STRING:
    {

    }
}

BUTTON_EVENT[dvTP, 100]
{
    PUSH:
    {
    IF(![dvQsys_Core, DEVICE_COMMUNICATING])
    {
        ON[dvTP,8] //NOT CONNECTED
        IP_CLIENT_OPEN (dvQsys_Core.PORT, Qsys_IP_Address, Qsys_Port, IP_TCP)
    }
    }
}

(*****************************************************************)
(*                                                               *)
(*                      !!!! WARNING !!!!                        *)
(*                                                               *)
(* Due to differences in the underlying architecture of the      *)
(* X-Series masters, changing variables in the DEFINE_PROGRAM    *)
(* section of code can negatively impact program performance.    *)
(*                                                               *)
(* See ?Differences in DEFINE_PROGRAM Program Execution? section *)
(* of the NX-Series Controllers WebConsole & Programming Guide   *)
(* for additional and alternate coding methodologies.            *)
(*****************************************************************)

DEFINE_PROGRAM


(*****************************************************************)
(*                       END OF PROGRAM                          *)
(*                                                               *)
(*         !!!  DO NOT PUT ANY CODE BELOW THIS COMMENT  !!!      *)
(*                                                               *)
(*****************************************************************)

Heres Diagnostics Output Bar contents:

Line 1 (17:35:11):: Exiting TCP Read thread - closing this socket for local port 3
Line 2 (17:35:11):: CIpEvent::OffLine 0:3:1
Line 3 (17:35:49):: Connected Successfully
Line 4 (17:35:49):: CIpEvent::OnLine 0:3:1
Line 5 (17:36:49):: Exiting TCP Read thread - closing this socket for local port 3
Line 6 (17:36:49):: CIpEvent::OffLine 0:3:1
Line 7 (17:44:40):: Exiting TCP Read thread - closing this socket for local port 3
Line 8 (17:44:40):: CIpEvent::OffLine 0:3:1

I greatly appreciate the help!

Comments

  • ericmedleyericmedley Senior Member - 3709 Posts Posts: 4,119
    A couple things might have something to do with it. And they both fall withing the "the code works logically, but not with the hardware" zone.

    Firstly, you initiate your first IP_Cleint_Open in the online event of the master (device 0) While this is technically okay, it assumes much in the way of what's actually ready to run at that time. I feel it's a good idea to wait a tad bit after initial bootup to start running your code. Let the thing finish all it's initialization first. It is entierely possible for device 0 to throw and online event but the NIC card hasn't finished the handshake with the switch/router. It might not be ready to open your connection yet or is really busy.

    In a similar vein, your method of trying to re-establish a lost connection is in the DATA_EVENT>ONERRO. when an onerror event if fired it's usually the result of something going wrong with the socket connection. Even LAN network latency can be on an order of several milliseconds. (20-40 is not uncommon) However, your processor is running more on an order of 3-5 million processes per seconds. So, by triggering the reconnect from an ONERROR event, you are literally staring to try to reconnect under a millisecond after the failure. Meanwhile, the two NICs are probably still trying to shut down from the failed romance.

    There are many ways to approach timing when it comes to your program outrunning your hardware. To be honest, most of us Netlinx programmers have gotten away with murder due to the slowness of our processors when compared to more computer-like things out there. But, now our processors are getting to the point where over running is a common occurrence.

    The way I tend to handle IP communications is this: (just talking 30,000 ft. methodology)

    I create a repeating timeline that will poll for the device. The speed of the poll depends upon the device. but for the sake of this discussion, let's say we poll the device every 10 seconds. The IP_Client_Open() command goes inside this Timeline. So every 10 seconds my master will reach out to the device. If the device connects I then Pause/Kill the timeline. I put a command in the OFFLINE event of the devices port (in your case 0:3:0) that will restart the polling timeline in the event the device closes the connection.

    Next, I create a startup sequence that runs at the appropriate time after boot up. I happen to always create a virtual device I call Debug at 33000:01:0 that I use for internal messaging and so forth. I use this virtual device's online event (plus a few seconds) to trigger the Startup function. In that function I will start all my IP Polls. So what happens is the poll begins trying. Once the NIC is ready or the device talks back, I then start talking to it as needed.

    If you choose this method you'll be assured that the Master will attempt to connect in a controllable manner that doesn't throw tons of errors because the program is stomping on the hardware.

    I also use timelines to create message queues so I don't over run serial buffers. There are still many devices that don't like too much chatter too fast. It's one of those things it's a good practice to get into and once you've written the timing and message queue code, it's a done thing and your can replicate it with ease.

    There are other ways to accomplish pretty much the same thing. These are just my ways.
  • zack.boydzack.boyd Smacks Keyboard Repeatedly Posts: 88
    ericmedley wrote: »

    I create a repeating timeline that will poll for the device. The speed of the poll depends upon the device. but for the sake of this discussion, let's say we poll the device every 10 seconds. The IP_Client_Open() command goes inside this Timeline. So every 10 seconds my master will reach out to the device. If the device connects I then Pause/Kill the timeline. I put a command in the OFFLINE event of the devices port (in your case 0:3:0) that will restart the polling timeline in the event the device closes the connection.

    Additionally, you're going to find you need to poll Q-sys as a keep-alive functionality. In my q-sys module, this is another way I'm checking for active comm to the box.
  • ericmedleyericmedley Senior Member - 3709 Posts Posts: 4,119
    zack.boyd wrote: »

    Additionally, you're going to find you need to poll Q-sys as a keep-alive functionality. In my q-sys module, this is another way I'm checking for active comm to the box.

    Good point. If you use my method this is easily done as well. In the polling timeline, you just add a conditional statement. if the connection is gone: do IP_Client_Open(). If the connection is good, send the "keep alive" message. In this case you just don't pause/kill the repeating timeline after a good connection. It will just run for the duration of your uptime.
  • dthomas32308dthomas32308 Independent Developer Posts: 41
    That's what I get for just trying to cut and dry sit down for 30 minutes to start learning new devices after a day of writing PLC code. Sad part is I have an IP comm include I typically use but for whatever reason did not.
  • dthomas32308dthomas32308 Independent Developer Posts: 41
    [USER="1623"]ericmedley[/USER] , I've always handled IP Connections primarily using functions but reading your way sounds a lot more efficient resource and speed wise. Any chance I could pick your brain as I explore your way of handling IP?
  • a_riot42a_riot42 AMX Wizard Posts: 1,619
    I don't really do any timing code for IP. The system tells you the status of sockets through events, so I just use them and respond accordingly, so there's no unnecessary delay. I don't typically try and keep sockets open, since the other side can close the connection at any time. If it closes, just open it again next time.
    Paul
  • ericmedleyericmedley Senior Member - 3709 Posts Posts: 4,119
    [USER="1623"]ericmedley[/USER] , I've always handled IP Connections primarily using functions but reading your way sounds a lot more efficient resource and speed wise. Any chance I could pick your brain as I explore your way of handling IP?

    Sure, we can probably do a phone call or something. Or post your code here. There are lots of good brains who can help.
  • daveruvolodaveruvolo Junior Member Posts: 7

    I handle my IP code a little differently then most have suggested. I create a buffer for what i want to send to the device. There is a timeline that checks for a delimeter in the buffer. If the delimiter exists and i'm not connected i initiate the connection at that time. They way i initiate my connections is always done in a call and i use a minimum of 2 variables to track it. The first variable is the attempting connection. I do this so if my timeline runs again before the connection is established, it isn't trying to open a second one because that will generate errors. once the connection is made i clear the first variable and set a second telling me the connection has been made. This is the same variable my timeline checks against to know if it needs to make a connection because there is a command to send. This assumes you don't need a keep alive but if you do just send a random query (that works) every 20 seconds or so (depending on the device).

    If it helps, I call my variables iAttemptingToConnect and iConnected. They can both be zero if you aren't trying to connect but they should never both be 1. I also typically add a 3rd variable for enabling/disabling my module. I do this so while the unit is being built/ tested and I don't need everything trying to communicate I can just turn the module off which also clears all the variables and kills all timelines. If the variable for the module status is disabled, it won't try and make the connection to the device even if something tries to initiate it.

    Hope this helps. I know this was a while back but hopefully someone can benefit.

  • viningvining X Member Posts: 4,301
    edited January 17

    Most folks do thing fairly similar, some use a queue array and chase their head with their tail and others use a buffer/string with delimiters like you and some just send from where ever the command is being generated which really isn't proper but often works just fine. I've done all myself but now almost always use a queue.

    I always just use 1 variable, usually in a structure but not always and I have constants for my states but I have many possibilities which often aren't all used or needed.

    DEFINE_CONSTANT //CLIENT CONNECTIONS 
    
    #DEFINE IP_CLIENT_CONSTANTS
    
    CHAR IP_CLIENT_DISABLED     = 0;
    CHAR IP_CLIENT_DISCO        = 1;
    CHAR IP_CLIENT_PENDING      = 2;
    CHAR IP_CLIENT_CONNECTED    = 3;
    CHAR IP_CLIENT_DEVICE_READY = 4;
    CHAR IP_CLIENT_RX_DATA      = 5;
    CHAR IP_CLIENT_TX_DATA      = 6;
    
    DEFINE_CONSTANT //SERVER CONNECTIONS
    
    #DEFINE IP_SERVER_CONSTANTS
    
    CHAR IP_SERVER_DISABLED = 0;
    CHAR IP_SERVER_STOPPED  = 1;
    CHAR IP_SERVER_PENDING  = 2;
    CHAR IP_SERVER_LISTENING= 3;
    CHAR IP_SERVER_CONNECTED= 4;
    CHAR IP_SERVER_RX_DATA  = 5;
    CHAR IP_SERVER_TX_DATA  = 6;
    

    When I open a socket:
    sMyDev.nConState = IP_CLIENT_PENDING;

    Online event handler
    sMyDev.nConState = IP_CLIENT_CONNECTED;

    If I then have to log in or negotiate, upon success
    sMyDev.nConState = IP_CLIENT_DEVICE_READY;

    Offline
    sMyDev.nConState = IP_CLIENT_DISCO;

    Something happens or there's a connection error
    sMyDev.nConState = IP_CLIENT_DISABLED;
    so I won't automatically try to recon until a wait ends or something else happens to let me know I can try again and then I go back to disco which means I'm ready to try and open a socket again.

    ETC,

    It is important to know your state and track it but sometimes it's more important to just get your code working and the job done. Back in the day there were a lot of coders to share ideas with and most all wanted to write better code but now a days most of those guys have moved on to other systems.

Sign In or Register to comment.