Home AMX User Forum NetLinx Studio

New to Programming

*First post, glad to be here! I'm excited to learn from you and look forward to interacting with everyone!

I have never programmed anything aside from a few page flipping Pronto remotes. I am learning to program with Netlinx by watching online videos on Harman University.
Apparently some of the information there is outdated.

The thing I'm trying to implement now is feedback that references variables.
The videos I have seen all instruct feedback statements to be placed in the "Define_Program" section. I've found information contradicting that in the NX-3200 documentation that I'm actually working with.

Here is the feedback I want to use:
[dvTP_SOURCEio,21] = nAUDIOSW_OutStatus
[dvTP_SOURCEio,11] = nAUDIOSW_Out1
[dvTP_SOURCEio,1] = nAUDIOSW_Out1

Since it is advised against placing these lines in the "Define_Program" section, where do I put them?

The research I've done is pointing me in the direction of creating a Timeline Event of some sort but I have not been able to figure out how to create one that applies, or if that is even the best approach.
I'm not asking anyone to do this for me. I need to learn and understand this myself, I just don't seem to have been able to find the proper reference material to learn about this. Thank you for your time and any guidance you may provide.

Comments

  • welcome to the NetLinx....world !!!!!
    The "Define_Program" section was being used on the previous masters series but for the new NX ones is not suggested. Instead, you have to make a timeline which will being fired every some seconds ( e.g. 1 second ).
    The timeline event will have the code being executed every that time (1sec). In your case, the code will be the feedback. Something like :

    timeline_event[TL]
    {
    switch (timeline.sequence)
    {
    case 1 :
    {
    [dvTP_SOURCEio,21] = nAUDIOSW_OutStatus
    [dvTP_SOURCEio,11] = nAUDIOSW_Out1
    [dvTP_SOURCEio,1] = nAUDIOSW_Out1
    }
    }
    }
  • Thank you George!

    I appreciate you helping me. Forgive my ignorance, but I literally have zero programming experience and my only references are what I can look up in the NS help files and the Harman University videos that have not yet mentioned this aspect of programming.

    I've watched all of the CP131 lessons and many others. Unfortunately this just hasn't been covered.

    From what you wrote, I see that it is an event so I would write this in the "Define_Event" section.
    How do I define the timeline duration? Should it repeat or should I trigger it with a button event relevant to the feedback in the timeline?

    timeline_event[TL] // What goes in the [TL] brackets? The device? [dvTP_SOURCEio] ? The defined timeline [TLfeedback]?
    {
    switch (timeline.sequence) // Is "switch" a conditional like an "if/else" ? What is the (timeline.sequence)? Do I define this somewhere? Is it a command?
    {
    case 1 : // Is this a condition? Do I define case 1? Is case one the lines that follow it?
    {
    [dvTP_SOURCEio,21] = nAUDIOSW_OutStatus
    [dvTP_SOURCEio,11] = nAUDIOSW_Out1
    [dvTP_SOURCEio,1] = nAUDIOSW_Out1
    }
    }
    }

  • ericmedleyericmedley Posts: 4,177
    Thank you George!

    I appreciate you helping me. Forgive my ignorance, but I literally have zero programming experience and my only references are what I can look up in the NS help files and the Harman University videos that have not yet mentioned this aspect of programming.

    I've watched all of the CP131 lessons and many others. Unfortunately this just hasn't been covered.

    From what you wrote, I see that it is an event so I would write this in the "Define_Event" section.
    How do I define the timeline duration? Should it repeat or should I trigger it with a button event relevant to the feedback in the timeline?

    timeline_event[TL] // What goes in the [TL] brackets? The device? [dvTP_SOURCEio] ? The defined timeline [TLfeedback]?
    {
    switch (timeline.sequence) // Is "switch" a conditional like an "if/else" ? What is the (timeline.sequence)? Do I define this somewhere? Is it a command?
    {
    case 1 : // Is this a condition? Do I define case 1? Is case one the lines that follow it?
    {
    [dvTP_SOURCEio,21] = nAUDIOSW_OutStatus
    [dvTP_SOURCEio,11] = nAUDIOSW_Out1
    [dvTP_SOURCEio,1] = nAUDIOSW_Out1
    }
    }
    }

    Firstly: Welcome to the forum. Secondly, I would highly recommend that you take the classes offered by AMX/Harman/Samsung. They cover a lot of these details and plus, you get certified in the process. Your boss (who is an AMX dealer I assume) can get you there for the classes with no cost other than your travel and hotel. Also, many of them are now done remotely right from the comgort of your laptoop.

    So to your question.

    I timeline is created, started, paused, stopped(killed) via a set of keyword/functions like timeline_create() timeline_kill(), et...

    The arguments in the timeline_Create() are:

    Timeline_Create(
    <a unique constant LONG value>,
    <a LONG integer array which denotes the various timed steps>,
    ,< a value for whether the time slots are absolute based upon the values in the arrar, or built relatively based upon the value of each cell from the previous>,
    < is the timeoline "one shot" or "repeating"> )

    there are built-in keywords for the last two: timeline_absolute / timeline_relative and timline_once / timeline_repeat.

    You have to declare your own Timeline IDs. I tend to format the constants like this:

    define_constant

    long TL_MyTImline_01= 1;

    Then for the timing array

    define_variable

    volatile long TL_MyTimeline_Times[]={1,2000,4000};

    so, in this cawse the timeline has three event sequences. They occur at tick 1 (essentially at the start), tick 2000 (2 seonds after the first sequence, and tick 4000 which is 4 seconds after the start.

    You basically call the Timeline_Create() whenever you want to start the timeline running and do a timeline_kill() when you're done. You can also pause, resume and so forth. You can even dynamically change the timings in the long array prior to starting the timeline to adjust when things happen on the fly.

    Hope that helps. And do get your boss to get you to those classes. You can thank me later.
  • [Deleted User][Deleted User] Harman Integrated Technologies Group (ITG) Posts: 0
    Charlie, for a visual on how to do what Eric is describing, keep going in your CP131 course. In Lesson 9, we cover how to set up Timeline Events. Because the course is building in concepts and complexity, we demonstrate simple DEFINE_PROGRAM feedback statements in Lesson 1, and then demonstrate the more advanced TIMELINE_EVENT statements in Lesson 9. The Exercise for that lesson is to move your statements out of DEFINE_PROGRAM and into a TIMELINE_EVENT.
  • TonyAngeloTonyAngelo Posts: 315
    You don't have to do it this way, the whole create a timeline that fires every x, is essentially a reinvention of the define program section.

    Another way to update the state of the button would be to just drop the state code right below where you update the variable value.
    nAUDIOSW_OutStatus = 1
    [dvTP_SOURCEio,21] = nAUDIOSW_OutStatus
    

    The problem with doing it this way, and the reason people use the timeline, is because doing the timeline method requires a single instance of the feedback code in the timeline to make it work for any updates to the variable anywhere in your code, so it makes the code easier to manage. This is because every time the timeline fires the gui will get all the current variable status.

    So how do you do this?

    A few things need to happen: you will need to define some constants/variables, you will have to define a timeline event, and you will have to create the timeline.

    Every timeline needs an ID to reference it by and this is best handled by defining a constant to use as the timeline ID. Every timeline also needs to know how long to run for before it fires it's event. There may be reasons to define the times as variables, as opposed to constants, but for this timeline a constant will be fine.
    define_constant
    
    tlMainline = 1 // can be any number, but every timeline in the system will need a unique number
    long lMainline[] = {350} // the timeline times need to be in an array of longs, the time is in milliseconds, so this one will fire every 0.35 seconds
    

    Next is defining the timeline event.
    define_event
    
    timeline_event[tlMainline]
    {
        // do your feedback stuff here, the code in here will run every time the timeline fires, no need for switch statements in this case
        [dvTP,1] = nPage
    }
    

    With that done all you have to do now is turn the thing on. Since this is emulating the mainline, you will want it to run continually, and you will need it to start every time the processor boots.
    define_start
    
    timeline_create(tlMainline,lMainline,1,timeline_absolute,timeline_repeat) // for this timeline the fourth input (timeline_absolute) is irrelevant, but the fifth needs to be set to repeat
    

    That's it. Now anytime in code that you update the value of nPage, [dvTP,1] will also update within 0.35 seconds.
  • ericmedleyericmedley Posts: 4,177
    TonyAngelo wrote: »
    You don't have to do it this way, the whole create a timeline that fires every x, is essentially a reinvention of the define program section.
    t

    I definitely agree on this point. I never use a repeating TL to do feedback. If you think about it - the only two times you need to do any feedback changes are 1) when something changes and 2) when a panel comes online or goes to a page/popup with feedback for the first time.

    So for me I just watch for those two things and update feedback only then. There is just no need to loop around

    the way I organize my feedback is I'll track all states in status vars. Those get updated when something changes on the device. immediately after the change has occured, the status var is updated, I then call the feedback .

    Feeddback functions are 1) fn_Update_Whatefver_fb(tp_id) and then 2) fn_Update_Whatefer_fb_All();

    the first function is really only called when the TP comes online or that specific page/popup is called firrst time after the TP comes online. Then the _aa() function just has a loop for all the UIs in the UI array. It cycles through each UI looking to see if it's online or not. If so, send the FB uipdate. It's very clean and low-bulk.

  • Wow! You guys are awesome. Thought I was alone here, glad to see there are so many talented people active on the forums and willing to help.
    Thank you Tony and Eric for the amazing step by step, kid gloves breakdown on how this is accomplished.

    Thank you Chris for pointing out Lesson Section 9. I feel like I just kinda got busted by the principle cheating on my homework. I thought I had studied all the CP131 videos minus a couple exercises. There are still about 15 I haven't watched including 3 lessons and 2 exercises concerning Timelines. Can't believe I didn't see that. Sometimes I get ahead of myself.

    I'll be diligently studying Lesson 9 and your step by step instructions in this post today until I have a working knowledge on this aspect of programming.

    Thank you all!



  • TonyAngeloTonyAngelo Posts: 315
    ericmedley wrote: »

    I definitely agree on this point. I never use a repeating TL to do feedback. If you think about it - the only two times you need to do any feedback changes are 1) when something changes and 2) when a panel comes online or goes to a page/popup with feedback for the first time.

    So for me I just watch for those two things and update feedback only then. There is just no need to loop around

    the way I organize my feedback is I'll track all states in status vars. Those get updated when something changes on the device. immediately after the change has occured, the status var is updated, I then call the feedback .

    Feeddback functions are 1) fn_Update_Whatefver_fb(tp_id) and then 2) fn_Update_Whatefer_fb_All();

    the first function is really only called when the TP comes online or that specific page/popup is called firrst time after the TP comes online. Then the _aa() function just has a loop for all the UIs in the UI array. It cycles through each UI looking to see if it's online or not. If so, send the FB uipdate. It's very clean and low-bulk.

    Yea, I really only use the "mainline timeline" for scheduled events and do all my feedback actions only when they need to happen.

    But this method is perfectly acceptable and I would suspect is the dominate one in use among AMX programmers.
  • Ok, so I studied everything in lesson 9 on Timelines in CP131 and your responses here. I have created a Timeline for feedback. I basically did it just as described in Lesson 9.

    I notice that in Tony's "Timeline_Create", in the 3rd parameter he used a 1 to indicate the number of times provided in the array. In lesson 9 it was stated that using keyword

    "length_array"

    was "a better method".

    This violated all my senses of efficiency and intuition but I wrote it as shown in the lesson instead of just placing a 1 there.

    As you may have guessed, I'm not a savvy forum poster so I'm unaware of how to post code in the grey boxes with a slide bar. So I edited together some screen shots and uploaded the file.

    Here are shots of my Timeline:


    I expect I will prefer to run this at shorter intervals than once per second. Something more like the 350 ms Tony recommended.

    I'll post an upload of the button events and feedback statements in the timeline_event next. Thank you!
  • Some things I'm not sure about.

    Can I reference a variable in an if/else conditional as anything other than a 1 or 0 as I have in these button events?

    Can I control more than 1 channel in a feedback statement by separating with a comma as I did in these feedback statements?

    Will this work as I intend or is there some noob fail in this code?


    I tried to convey my intentions with each line in the commented out text next to each line.
  • ericmedleyericmedley Posts: 4,177
    Ok, so I studied everything in lesson 9 on Timelines in CP131 and your responses here. I have created a Timeline for feedback. I basically did it just as described in Lesson 9.

    I notice that in Tony's "Timeline_Create", in the 3rd parameter he used a 1 to indicate the number of times provided in the array. In lesson 9 it was stated that using keyword

    "length_array"


    The reason to use length_array(My_Timing_Long_Array) has more to do with the larger goal of writing code that is more virtualized as opposed to 'hard coded" What I mean by this is (taking the example of the length_array) it's better to write your code in such a way that scaling it to allow for changes in size or scope is easy and less prone to 'gotchas' and errors.

    so, follow me down this rabbit hole as I tell the story.

    So, you're writing a timeline routine where you want three things to happen (say a projector On sequence) You know that you need three things to happen: Power On command, Input Select Command, and Aspect Ratio command.

    so you set up your timeline to do three steps in the proper amount of time to send each command when you want.

    You set up your timing array something like
    My_Timing_Array[]={1,15000,16000}  // power on at 1, input select at 15 seconds, and aspect at 16 seconds
    

    so you'd write your timeine_create as follows.
    timeline_create(TL_MY_TV_On,My_Timing_Array,3,time line_absolute,timeline_once)
    
    

    Okay fine, everything works. But a month later you realize you also need to send that annoying Eco Power Saver Feature Off so the dumb thing doesn't ignore your power on command to save the planet. No big deal. You just add another cell to the timeing array and put the new forth command in the timeline, right?
    My_Timing_Array[]={1,15000,16000,17000}  // power on at 1, input select at 15 seconds, and aspect at 16 seconds, Eco Mode off at 17 secibds
    

    But then you forget to run over to your timeline and change the "3" to a "4"
    timeline_create(TL_MY_TV_On,My_Timing_Array,3,time line_absolute,timeline_once)
    
    

    It compiles just fine. And you spend the next hour trying to figure out why the stupid thing never fires the fourth command.

    But, if you always try to virtualize your code, you don't have to worry about changing the size or scope of your timeline. By rewriting your timeline_create to
    timeline_create(TL_MY_TV_On,My_Timing_Array,length_array(My_Timing_Array,timeline_absolute,timeline_once)
    

    you can tweak or change the timeline size all you want and it will always work.

    In fact you should always take this approach in all your code. The trick is to catch yourself in the act. Any time you find yourself entering in a hard number for something - don't.

    esamples:

    button_event[TP,3]
    send_level TV,1,MyVolume
    channel_event[vdvDevice,199]

    change those numbers into constants that can be altered later if need be without breaking your code. You might think you can easily remember all these hard-coded numbers but trust me - later when you've written tens of thousands of lines of code, you'll forget where you did a lot of that.

    In addition, the same thing can be practiced for devices and functions.

    if you find yoruself writing

    button_event[TP,My_Power_Off_button]

    You're only writing code for one touch panel. so, say you've written an entire system and the client loves it. they love it so much they want to add a second touch panel. Now you're stuck going through and fixing all these single button events to include the new panel.

    I always write my code with device arrays, especially for touch panels. (Even when I know there's only one panel)

    DEFINE_DEVICE
    
    TP_01=10001:01:0
    TP_02=10002:01:0
    TP_03=10003:01:0
    //  etc....
    
    DEFINE_VARIABLE
    
    volatile dev TPs[]={
      TP_01
      ,TP_02
      ,TP_03
      // etc...
      }
    
    
    DEFINE_EVENT
    
    button_event{TPs,My_Power_Button]{
      push:{
        stack_var integer tp_id;
        tp_id=get_last(TPs)// the id of which tp pushed the button
        fn_Process_My+Power_Button_Push(tp_id);
        }// push
      }// b_e
    

    this way, if the client adds another touch panel later, or even 20 panels, your code automatically grows with it without changing one line.

    That's a lot of discussion over one simple item, but I think understanding the 'why' behind it helps make better sense of why we use it. It's all about making your code easily scaleable.






  • TonyAngeloTonyAngelo Posts: 315
    Using length_array is indeed the correct way to do it. In the example above, I hardcoded it for simplicities sake and also because I've never changed the length of the mainline array before.

    But as a rule, any time you use some arbitrary number somewhere in your code, like the 1 in the timeline_create, you should ask yourself "will this number ever change?" and if it might, you should not hardcode it but use a variable or function instead.
  • As a relatively weak counterpoint, I'll note that Netlinx is, at it's heart, a numeric token based language. Pretty much every real thing you deal with is represented by a number. While I make heavy use of structures and arrays for most of my work, there are places in Netlinx where the number is what the number means. I find the textual abstraction of numbers itself cumbersome sometimes.
  • Makes perfect sense for scaling.
    I also feel like the way the my mind works that Derricks counterpoint is something I can relate too.
    I certainly intend to follow the lesson advise for now. I know I don't know nearly enough about this to decide what to represent with a constant and when its all the same to just use the number. I suppose everyone has a nuanced style of coding with experience and time within a given programming language.

    Am I to assume that there is nothing blatantly wrong with the code I've written for button events, feedback and timeline that I posted since there is no comment from you guys addressing what I wrote or how it is written?

    Thank you all for your insight and help!

    Edit - I've been thinking about the code I posted screenshots of and am anxious to get feedback from you. It occurs to me that my feedback statements may not work the way I have written them with the intention of turning on or off multiple channels in the same statement. I'm thinking those channels may need to be an array to get the result I was going for.

    Short of making them into an array I could just write a separate statement for each channel
    [dvTP_SOURCEio,1]  = (nAUDx_Out1==1)
    [dvTP_SOURCEio,11] = (nAUDx_Out1==1)
    [dvTP_SOURCEio,2]  = (nAUDx_Out1==2)
    [dvTP_SOURCEio,12] = (nAUDx_Out1==2)
    [dvTP_SOURCEio,21] = (nAUDx_Global_Out)
    

    Am I on the right track here, or would it have worked the way I wrote them previously?
    Thank you again.

    PS - figured out the grey box =)
  • Looks exactly like my code (right or wrong). It's old-school programming techniques I learned at AMX many years ago. These statements used to go in the "Define Program" area, until the NX series processors were introduced. Now, I put them in a repeating timeline with about a 150 millisecond refresh rate. I come from a database background, so I'm kinda fixated on the idea that an action process writes to the database, then feedback is based off the value being read back out of the database. The asynchronous feedback statement loop works well with my approach. It's got it's drawbacks though; it basically doubles your potential failure points in code, spread out in 2 different places.
  • viningvining Posts: 4,368
    Looks exactly like my code (right or wrong). It's old-school programming techniques I learned at AMX many years ago. These statements used to go in the "Define Program" area, until the NX series processors were introduced. Now, I put them in a repeating timeline with about a 150 millisecond refresh rate. I come from a database background, so I'm kinda fixated on the idea that an action process writes to the database, then feedback is based off the value being read back out of the database. The asynchronous feedback statement loop works well with my approach. It's got it's drawbacks though; it basically doubles your potential failure points in code, spread out in 2 different places.
    I pretty much do the same, write and fb back is based on what was written. Eventually you?ll also want to track panels and only send feedback to those online or on a specific page. Update panels when they come online or go onto a page. Obviously you have to start with just getting proper feedback. Sometimes when a device is slow to respond some folks with temporarily provide FB based on the button pushed and then vslidate it when the slow device responds. This appeases anxious users who expect immediate FB. I can?t say I?ve came across anything so slow that needed this though.

  • What *IS* the penalty for trying to send feedback to an offline panel? Having panels that may be offline is a condition I've never really had to deal with. Almost all my systems are just one panel. I do track the online state, so it would be trivial to encapsulate any panel's feedback in a conditional. One of the only places I lay "fake" feedback on top of real feedback, is for projector transitions.
  • Yeah, sorry, I just deleted my post, although Eric's quote will save it for posterity. I am unconventional, there's no doubt about that. I'm an unconventional void-filler. I step in where there is nothing, and use my own ingenuity to create something. Too often, an aspect of that "void" is a lack of resources, and my work is basically re-inventing some wheel that already exists, I just don't have practical access to that wheel in my environment without re-inventing it myself. I've always really excelled in that phase of my typical job trajectory. But once I've filled the void, once I've accomplished enough to attract attention, my unconventional approach always becomes an issue. It's led to a lot of career disappointment, and a particularly sore spot in my psyche. It's not just this job, it's a theme that's run through my entire life.
  • ericmedleyericmedley Posts: 4,177
    Yeah, sorry, I just deleted my post, although Eric's quote will save it for posterity. I am unconventional, there's no doubt about that. I'm an unconventional void-filler. I step in where there is nothing, and use my own ingenuity to create something. Too often, an aspect of that "void" is a lack of resources, and my work is basically re-inventing some wheel that already exists, I just don't have practical access to that wheel in my environment without re-inventing it myself. I've always really excelled in that phase of my typical job trajectory. But once I've filled the void, once I've accomplished enough to attract attention, my unconventional approach always becomes an issue. It's led to a lot of career disappointment, and a particularly sore spot in my psyche. It's not just this job, it's a theme that's run through my entire life.

    I deleted my posts to honor your wishes to delete your post.
  • viningvining Posts: 4,368
    What *IS* the penalty for trying to send feedback to an offline panel? Having panels that may be offline is a condition I've never really had to deal with. Almost all my systems are just one panel. I do track the online state, so it would be trivial to encapsulate any panel's feedback in a conditional. One of the only places I lay "fake" feedback on top of real feedback, is for projector transitions.
    FYI in my previous post where I quoted you and said I did things similar the rest of my post was for the OP not you. I wasn?t trying to say what you should do but what the OP can aim for in his or her code writing endeavors. As fsr as sending to offline panels I think that?s what most people do and there really isn?t anything wrong with it, it works fine. Some of us are just anal OCD control ffreaks that might be doing more harm than good but it satisfies our OCD and narcassistic need to control everything.
Sign In or Register to comment.