Results 1 to 6 of 6

Thread: How to Code for FuseGL using the power of C# [EXTREMELY USEFUL DOCUMENTATION]

  1. #1
    Fusion Brain Creator 2k1Toaster's Avatar
    Join Date
    Mar 2006
    Location
    Colorado, but Canadian!
    Posts
    10,052

    How to Code for FuseGL using the power of C# [EXTREMELY USEFUL DOCUMENTATION]

    FuseGL is the software used to control the Fusion Brain that is provided by me at Fusion Control Centre.

    The most powerful and unique feature of FuseGL software that took over 2 years to get working is something that nobody will ever see. But what it allows is for you to write code using Microsoft's .NET language of C# that controls the Fusion Brain and other aspects of FuseGL without having to re-invent the innards every time. It also allows a community to grow by sharing these code files (*.cs extensions). Once you write one, it will become easier. And by using Microsoft's power tool, Visual Studio, it is almost like using telepathy after getting down the basics. So even though it is not required, I highly recommend it and will direct this tutorial as if you have downloaded it. You can use Notepad if you prefer, but you loose all the really cool features that Visual Studio has to offer. The biggest such power is "Intellisense" which automatically starts to fill out what you are typing as you are typing. This is something you should do on your development PC, and then when finished move to your CarPC or Home Automation PC. There is no harm to have it on any computer, but it is a hefty installation from Microsoft at 2.4GB of disk space and about 200MB worth of internet downloads.

    So first things first, you will need to download FuseGL from my website. All the links are the same, but packaged either raw or in an archive format. Your choice which one you choose.

    EXE [18.8MB]: fusioncontrolcentre.com/FuseGL/FuseGL.exe
    RAR [11.1MB]: fusioncontrolcentre.com/FuseGL/FuseGL.rar
    ZIP [11.4MB]: fusioncontrolcentre.com/FuseGL/FuseGL.zip

    You will need at least version 1.0.0.469 of FuseGL for this to work which most people should. The best way to tell the version number is by looking at the version tab under the properties of the exe. Open a windows explorer window and browse to the executable's location. Then right click and choose "Properties" from the list (usually on the bottom):



    Then go to the "Details" tab, and look under "File Version":


    1.0.0.470 is up to date, so we can use it. Another way to check is to run the EXE. After it loads and the display appears, close the program and look for the file FuseGL_Trace.txt made in the applications root directory. The first line will look something like:

    Code:
    Fusion Control Centre -- FuseGL {1.1.0.470}
    If your version is old, then allow it to update. Connect your machine to the internet, and run FuseGL. Wait a little bit after you launch it and you should see a little popup window saying there is an update available:


    Let it download automatically. It will then close the application, update, and then re-launch itself. Then check the exe to make sure the version number is correct.

    You will also need 1 other DLL [FB_USB_2011.dll] that is bundled in the exe, but you will want separately. You can download that here: http://www.fusioncontrolcentre.com/F...B_USB_2011.dll

    The other important tool you need is Visual Studio C# Express. You can download Visual Studio 2010 C# Express for FREE from this page: http://www.microsoft.com/visualstudi...d-2010-express


    If you want the direct link and do not want to find it yourself, then click here: http://go.microsoft.com/?linkid=9709939

    This will download "vcs_web.exe":


    Then run that exe and it will start loading:


    Eventually this window will pop up to start the installation of Visual Studio 2010 C#:


    Read and accept the license terms. If you don't except the license terms, you will have a very painful learning curve in another tool suite:


    You do not need any of the optional components, but they don't hurt. It is up to you, I chose to deselect them:


    Let's you know it is a big download at 158MB and will take 2.4GB of disk space. It is totally worth it. I would keep the installation folders default, it keeps it easier in the future for plugins if you so want them:


    It will then start the download and installation process. It will probably take 20+ minutes on an average computer:


    After it downloads, it will begin installing:


    You will more than likely need to reboot your machine in the middle of installation. Follow the prompt:


    Wait wait wait... You did reboot right? If you are like me, you take reboot warnings and requirements as loose suggestions and ignore them completely. Yes you actually need to reboot. If you do not see your starting splash logo, you didn't do it right. Reboot all the way. Yes it is painful but you need to do it. Sorry:


    It should just pick up your installation where it left off:


    Eventually it will finish installing:


    Now run the program:


    The first time you run it the splash may stay a while and you might see a setup of your initial workspace:



    Now you are ready to start coding. This is where you should start if you revisit this tutorial later with everything already installed. I HIGHLY recommend actually typing in the code, not copy and pasting. This will let you see what the intellisense is doing and you might get better ideas. You can read the meta data and so on and gain experience rather then just going through the motions and remaining confused.

    So make a new Project. Go to "File" then "New Project" from the top menu bars:


    Make sure that "Visual C#" is chosen as your template and then choose "Class Library" from the pane on the right. Name it something more useful then the default of "ClassLibrary1.cs". I'm naming mine HVAC_Control_Example



    Eventually you will get something like this:


    This is the default class view where you will be able to code. Right now it defaults to a generic class name and adds some default "using" statements that it thinks you might like or need. Lets change that class name to something more meaningful like "HVAC_Example". Left click to select "Class1.cs" from the Solution Explorer, the pane on the right.


    Then right click and select Rename from the menu:


    Remember to keep the *.cs, this is IMPORTANT! You will notice how Visual Studio is smart and asks you if you want to rename other things. Say yes please Bill Gates:


    Notice in the code area that "Class1" has been changed to "HVAC_Example". It has happened in other areas too that we won't go into as it is not too important. But just accept it and like it:


    Now we need to add references. One reference to FuseGL itself, and another to a DLL that FuseGL uses that you downloaded earlier (FB_USB_2011.dll). Left click the "References" tree node in the Solution Explorer, then right click, then choose "Add Reference" from the drop down menu.


    My exe and dll are located in a folder called "FuseGL" on the desktop. So select "Browse" from the tab, navigate to that location and then add those files. You can add them individually or select multiple files like any windows explorer window by holding shift or control to select groups and multiple files.


    After they add you will see them in the References tab in the Solution Explorer if you expand the tree


    Now they exist in our project. This tells our code that we may or may not reference these files. But if we do, you can look for them in the locations we selected (my Desktop/FuseGL folder). If you move the location of the references, you will need to change the reference location. Now we can use these by typing in full class names. That is not always convenient, so lets add some using statements. These are like shortcuts. So if your file had "a.b.c.d.1.2.e" you would have to say that whole thing every time you wanted to reference it. But sometimes you might just want to say "2.e". In that case your "using" statement would go down from "a.b.c.d.1". It is like a little shortcut to save on typing. Some Visual Studio plugins resolve these automatically like ReSharper which is worth the money if you do this all day long like I do. So to begin with you should have at least these using statements already:
    Code:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    These are found on the top of your code (they must go at the top of the file before anything else other than comments, like copyright stuff):


    Now lets add these:
    Code:
    using FB_USB_2011;
    using FuseGL.GUI;
    You may notice a couple things. First, caps are important. FuseGL is not the same as fusegl. So typing is important. If you mistype something you will see angry lines like these:


    This means you have an error. It will also show up in the error window. But we can't see that because it is not enabled by default. Lets enable it. Go to View, Other Windows, Error List


    Now you will see the Error Window below like this:


    Notice how it shows the description of the error, what file it is in, what line it is on, and even the column of the character the error starts with... Pretty impressive for a compiler. If you have never done this before, you may not realize just how glorious this is. In the old days, even a few years ago if you had an error it may compile but then just not work. Now it will tell you many ways. You can get the description by just hovering over the angry lines in the text as well.

    Another thing you may have noticed is that if you typed "FuseGL" properly, as soon as you hit the "dot" (.), a window popped up. This is intellisense that I was talking about earlier.


    This is where the meat and potatoes is. You can learn everything you need by poking around these menus. You cannot type in "FuseGL.Zebra". Well you could but you would get an angry underline, and a compile error so nothing will work. You know that you can type in "FuseGL.Configurator", "FuseGL.FusionBrainOnline", "FuseGL.GUI", "FuseGL.Input", "FuseGL.Logic", or "FuseGL.Update" and that's all. You don't get this from Notepad. So you could type in the same thing and have it work in Notepad, you wont get this great map that narrows it down from anything you can possible type on a keyboard to these 6 options. It makes coding so much easier, safer, and faster!

    So something that is periodically good to check (and Visual Studio does it automatically for you too) is to make sure your code still works after you make some changes. You can tell this by performing a "build". We won't actually use the output that the compiler makes, but if it builds here, it will work in FuseGL unless you have a logical error. But syntactically it will be correct. You know you are missing a semi-colon somewhere (something Visual Studio checks for even) or put a bracket in the wrong spot. So go to Debug, then Build Solution from the top menu bar:


    If it works, and you are on a fast computer, the only indication that it worked is the tiny little "Build Successful" message in the bottom left corner of the window. If you have a slower PC, you might see it say building with a progress bar, but once it is done this is what will remain. If you made a coding error and something bad happens, the Error List we made display before will show you the lines and descriptions of the errors. Keep in mind that in general you may have an error that masks other errors. So it may show 1 error because you have 1 really bad error. When you fix that, then you have 10 errors. It could not find those 10 errors before because that 1 bad error was preventing it from knowing. So only when you have 0 errors, do you actually have no errors. 1 error means, you have at least 1 error.
    Last edited by 2k1Toaster; 01-28-2013 at 11:32 AM.
    Fusion Brain Version 6 Released!
    1.9in x 2.9in -- 47mm x 73mm
    30 Digital Outputs -- Directly drive a relay
    15 Analogue Inputs -- Read sensors like temperature, light, distance, acceleration, and more
    Buy now in the MP3Car.com Store

  2. #2
    Fusion Brain Creator 2k1Toaster's Avatar
    Join Date
    Mar 2006
    Location
    Colorado, but Canadian!
    Posts
    10,052
    So now lets code!

    You can do this so many different ways. That is the beauty of this. If you want to do it one way or the other, you can. You can download examples from the web, or even hire a professional coder if this is going in infrastructure. Anything that Microsoft allows you to do with .NET and C#, you can do with this. That means you can even call managed wrappers for the WIN32 API and get low level hooks, or show webpages, or literally ANYTHING. In this example we will make a simple fan speed controller. We will have 1 button that when you click it will cycle through fan speeds. We can choose those fan speeds to be low, medium, and high for the purposes of this demo. You may have 2 speeds or 20 speeds, you can do what you like. You may want 1 button for each, or maybe 1 button for every 4, it is completely up to you. Maybe you want to do things like turn on the AC if the defroster is turned on. Or if the fan is set to off, maybe change the air to recirculate first. The world is your oyster as exposed by FuseGL and the power of the entire .NET C# language.

    So lets create something smart that knows what state the fan speed is in. We could use a variable like an integer. Say 0 is off, 1 is low, 2 is medium, and 3 is high. But do you really want to remember that everywhere? Wouldn't it be nice to do things like "if fanspeed is HIGH then do something" instead of "if fanspeed is 3 then do something". 3 is meaningless, HIGH is a meaningful value. What if fan speed is 9? That doesn't make sense, and is an opening for a bug. This is what enumerations are for. So lets make an enum (code word for enumeration) that stores the different values of fan speeds.

    Your code should reside in your class, which is this space here:

    You should put your code between the opening and ending brackets. This is the "{" (opening) and "}" (closing) characters. These "frame" your code. Things that make decisions or hop/skip over parts of code like if statements, or loops use these as their bounds. Your main boundary is your class, so everything you should type for this example should reside between these two brackets. You will notice that they are tabbed out. The further you go in, (the depth) the more indentent it should be. Every level in, gets 1 tab in. That way your code is readable. You can put everything on one line, C# like the honey badger don't care. But it makes it a PITA to debug or read.


    So lets put our enum in there. we start with "public". This means other things can use it. If it was not public, then nothing in a less deep (shallower/higher/whatever) level could use it. Only things one or more "{"'s deep could see and use it. Otherwise there would be an error. Public says anyone can use it. Everything you can see in my code when we added references is public. There are thousands of things in there you cannot see or mess with because you should not be able to. If you find that is the case, then you can use "private" or just omit the qualifier which defaults to private. You can also use "internal" which means public for everything in this file, but if another file uses it, then it is private which can be useful and easy. We then say "enum" which is the syntactical word for enueration then we call it something. You can call it whatever. Please don't use short names like "a" and "fs" or something. You save yourself a couple bytes but a year from now, you will have no idea what you were writing. Use big long names that are descriptive. You should be able to read your code like a novel/magazine. Not like a manual where you need to lookup and index every few characters with footnotes and so on.

    Code:
            public enum FanSpeed
            {
                OFF,
                LOW,
                MEDIUM,
                HIGH
            }


    Now every fan speed probably has its own output if you are cannibalizing a preexisting controller. So OFF, LOW, MEDIUM, and HIGH need their own output. We probably also want them to have their own image so you know what image you are on. So these are groups right? Every fan speed should have both an output and a picture. So lets group the output and a picture. Then we can pass around this object that contains both things of information and not have to worry about passing both in and keeping track of it ourselves. You could, but it would get complicated fast later on. This is exactly what a class is for. So again we will do a public class and follow it with a name. Then inside the brackets we will say use a texture (the picture) and a digital output. You can find the exact syntax easily because you are able to poke around the intellisense. Otherwise this would be very difficult. Make sure to make your variables for the DigitalOutputPort and the FGLO_Texture are public because they are another layer down deep but we need to use this in things above it in the heirarchy. So make it public. You will also notice how Visual Studio colour codes things and automatically indents to keep the heirarchy. If it stops doing this, you have a big problem in your code somewhere you should fix first.

    Code:
            internal class MyInternalHVAC_FanSpeed_Class
            {
                public FB_Monitor.DigitalOutputPort DigitalOutputPort;
                public FuseGL.GUI.FGLO_Texture FGLO_Texture;
            }


    Now we are going to add a constructor for this class. A constructor is something that describes how to build it. We need both an image and a port for this class, we don't want some that only have ports but not images, or only images but no ports. We could be careful and code it that way, but it would be better if we disallow it completely. That way you in 4 years will not make that mistake or if you give it to somebody, they won't make that mistake either. A constructor is a method that has the same name as its class and no return type. It has to be public because the only thing constructing it is outside! So our constructor will take in a DigitalOutputPort and a FGLO_Texture and then all it does is store what it takes in to inside itself and keeps them safe and combined.

    Code:
            internal class MyInternalHVAC_FanSpeed_Class
            {
                public FB_Monitor.DigitalOutputPort DigitalOutputPort;
                public FuseGL.GUI.FGLO_Texture FGLO_Texture;
    
                public MyInternalHVAC_FanSpeed_Class(FB_Monitor.DigitalOutputPort digitalOutputPort, FGLO_Texture fgloTexture)
                {
                    DigitalOutputPort = digitalOutputPort;
                    FGLO_Texture = fgloTexture;
                }
            }


    So now lets make something that uses these two things, Fan Speed, and a grouping of an image and output, and correlates them. We will use a dictionary for that. Like a real dictionary, it has a key and a value. We will know what the fan speed is, and then tell the program to change the digital outputs and the image displayed to the user depending on what the fan speed is. So if the definition (value) was the MyInternalHVAC_FanSpeed_Class and we looked it up by the FanSpeed key, that would make things easier.

    So lets do that. The syntax is:

    Code:
    internal Dictionary<FanSpeed, MyInternalHVAC_FanSpeed_Class> FanSpeedLookupDictionary;
    So we have an internal dictionary that takes a FanSpeed as the key and our special class as the value, and we called in FanSpeedLookupDictionary. Now we want this to be something when the class gets called, right now it doesn't exist. It is like saying we want to use something that looks like this, but we don't know what is in the dictionary. If you then try and read the dictionary but you don't know which dictionary, you will get an error. So lets make a new dictionary, that is empty. So now it exists, but it is empty. That is better then not existing and having no information about it. Now we know it is empty and there. We can do this on the same line as above, so lets extend that line.

    Code:
    internal Dictionary<FanSpeed, MyInternalHVAC_FanSpeed_Class> FanSpeedLookupDictionary = new Dictionary<FanSpeed,MyInternalHVAC_FanSpeed_Class>();


    We also want something to store what the current fan state is. It should be of type FanSpeed that we made earlier. So lets just make this simple variable. And lets make its default value OFF. Notice how we don't say 0, we say "FanSpeed.OFF". Isn't that much easier to read? Create a new internal variable of type FanSpeed called currentFanSpeed and set it equal to FanSpeed's enumeration value of OFF. Rolls right off the tongue.

    Code:
    internal FanSpeed currentFanSpeed = FanSpeed.OFF;


    Now lets make a method that sets up the HVAC settings. This will be called when FuseGL starts. This is your opportunity to bind to objects that FuseGL creates at the beginning. You only need to do it once, but you need to know where on the screen you can draw your button, and which digital outputs control the fan. FuseGL will take care of actually drawing the image, and finding the Fusion Brain and talking to it. You just tell it what value you want it to be at.

    So lets do something like this:

    Code:
    public void setup_HVAC(
                                    FuseGL.GUI.FuseGL_Object displayFuseGL_Object_for_fan_speed,
                                    FB_Monitor.DigitalOutputPort _digitalOutputPort_for_fan_off, 
                                    FB_Monitor.DigitalOutputPort _digitalOutputPort_for_fan_low, 
                                    FB_Monitor.DigitalOutputPort _digitalOutputPort_for_fan_medium, 
                                    FB_Monitor.DigitalOutputPort _digitalOutputPort_for_fan_high
                                  )
            {
                // Setup FanSpeed textures and ports
                FanSpeedLookupDictionary.Add(FanSpeed.OFF, new MyInternalHVAC_FanSpeed_Class(_digitalOutputPort_for_fan_off, FGLO_Texture.MakeQuickFGLOTexture___ConvertFromExternalImagePath("HVAC_Fan_Control___FanSpeedImage___OFF", "images/HVAC/FanSpeed/Off.png")));
                FanSpeedLookupDictionary.Add(FanSpeed.LOW, new MyInternalHVAC_FanSpeed_Class(_digitalOutputPort_for_fan_low, FGLO_Texture.MakeQuickFGLOTexture___ConvertFromExternalImagePath("HVAC_Fan_Control___FanSpeedImage___LOW", "images/HVAC/FanSpeed/Low.png")));
                FanSpeedLookupDictionary.Add(FanSpeed.MEDIUM, new MyInternalHVAC_FanSpeed_Class(_digitalOutputPort_for_fan_medium, FGLO_Texture.MakeQuickFGLOTexture___ConvertFromExternalImagePath("HVAC_Fan_Control___FanSpeedImage___MEDIUM", "images/HVAC/FanSpeed/Medium.png")));
                FanSpeedLookupDictionary.Add(FanSpeed.HIGH, new MyInternalHVAC_FanSpeed_Class(_digitalOutputPort_for_fan_high, FGLO_Texture.MakeQuickFGLOTexture___ConvertFromExternalImagePath("HVAC_Fan_Control___FanSpeedImage___HIGH", "images/HVAC/FanSpeed/High.png")));
    
                // Hook into when the FGLO is pressed
                displayFuseGL_Object_for_fan_speed.MouseEvent___ClickDown += new FuseGL_Object.FuseGL_FGLO_MouseEvent(ChangeHVAC_FanSpeed_Button_Was_Pressed);
    
                // Change the image to something known to begin with
                ChangeFanSpeed(currentFanSpeed, displayFuseGL_Object_for_fan_speed);
            }


    Note that the double slashes are comments. You can litter your code with these to tell other people or usually your future self what the heck you were thinking at this moment in time. You will have a few errors since we are telling it to call and use other methods and variables that don't even exist yet. Lets break it down...

    Code:
    public void setup_HVAC(
                                    FuseGL.GUI.FuseGL_Object displayFuseGL_Object_for_fan_speed,
                                    FB_Monitor.DigitalOutputPort _digitalOutputPort_for_fan_off,
                                    FB_Monitor.DigitalOutputPort _digitalOutputPort_for_fan_low,
                                    FB_Monitor.DigitalOutputPort _digitalOutputPort_for_fan_medium,
                                    FB_Monitor.DigitalOutputPort _digitalOutputPort_for_fan_high
                                  )
    This is the method prototype. It sets up what you need for this function. It is like an ingredient list in a recipe. This particular method/recipe needs 1 FuseGL.GUI.FuseGL_Object that we will refer to as "displayFuseGL_Object_for_fan_speed", and 4 FB_Monitor.DigitalOutputPort's that we will refer to as "_digitalOutputPort_for_fan_off", "_digitalOutputPort_for_fan_low", "_digitalOutputPort_for_fan_medium", and "_digitalOutputPort_for_fan_high". If you want to call this method and use it, you must supply 4 parameters and they must be of this kind. You can't give this method 4 badgers and a llama and have it work. It very specifically says what it needs, and you need to adhere to it.

    Then we do this 4 times slightly differently each time:
    Code:
    FanSpeedLookupDictionary.Add(FanSpeed.OFF, new MyInternalHVAC_FanSpeed_Class(_digitalOutputPort_for_fan_off, FGLO_Texture.MakeQuickFGLOTexture___ConvertFromExternalImagePath("HVAC_Fan_Control___FanSpeedImage___OFF", "images/HVAC/FanSpeed/Off.png")));
    There is a lot in that little line. We are taking that empty dictionary and adding something to it. So after every line, there is 1 more thing in the dictionary. We are adding a definition (value) for the key (thing to look it up by).

    Code:
    FanSpeedLookupDictionary.Add(KEY, DEFINITION);
    You will notice that the definition part has a lot to it, so lets look at that:
    Code:
    new MyInternalHVAC_FanSpeed_Class(_digitalOutputPort_for_fan_off, FGLO_Texture.MakeQuickFGLOTexture___ConvertFromExternalImagePath("HVAC_Fan_Control___FanSpeedImage___OFF", "images/HVAC/FanSpeed/Off.png"))
    That whole thing is the definition. Remember we were relating FanSpeed enumerations to the other class we made, MyInternalHVAC_FanSpeed_Class. So that's what we are doing. We are creating a new MyInternalHVAC_FanSpeed_Class. But remember we also made a contract that said when we make on of these MyInternalHVAC_FanSpeed_Class classes, we need to pass in a digital output port and a texture otherwise it wont work. So we are:

    Code:
    new MyInternalHVAC_FanSpeed_Class(SOME DIGITAL OUTPUT PORT, SOME TEXTURE)
    Notice how the digital output port is one of the ones we passed into this setup method. It will control the off. Now we pass in a texture. What texture? Well we have to make one. So we are making one:

    Code:
    FGLO_Texture.MakeQuickFGLOTexture___ConvertFromExternalImagePath("HVAC_Fan_Control___FanSpeedImage___OFF", "images/HVAC/FanSpeed/Off.png")
    We are actually calling a method called MakeQuickFGLOTexture___ConvertFromExternalImagePat h that just happens to return a Texture. But to call it, we need to pass in a string (the things in the double quote marks) that tells the program what the image is called, and where the image is. You can get all this information as you type out that long line (you did type it manually right?!?!) via intellisense that gives a bit of explanation:



    So that whole line...

    Code:
    FanSpeedLookupDictionary.Add(FanSpeed.OFF, new MyInternalHVAC_FanSpeed_Class(_digitalOutputPort_for_fan_off, FGLO_Texture.MakeQuickFGLOTexture___ConvertFromExternalImagePath("HVAC_Fan_Control___FanSpeedImage___OFF", "images/HVAC/FanSpeed/Off.png")));
    ... added the information we need to do something when the fanspeed is off. We will turn that digital output port on, and we will change the image to the image we created in that line. We then do the same for the low speed, medium speed, and high speed.

    The next section is what allows us to wait for a button press:

    Code:
                // Hook into when the FGLO is pressed
                displayFuseGL_Object_for_fan_speed.MouseEvent___ClickDown += new FuseGL_Object.FuseGL_FGLO_MouseEvent(ChangeHVAC_FanSpeed_Button_Was_Pressed);
    In the variable displayFuseGL_Object_for_fan_speed there is an event called MouseEvent___ClickDown that fires when the mouse is clicked down over that FuseGL Object. We don't want to check every image and see when the mouse is in there, and then notice when it has been clicked or not. That would be a lot of work. I've done all that for you and then I tell you when. So instead of you doing "are you clicked yet?" over and over you just wait and say, "hey FuseGL, when or even if this event happens (clicking of this object) let me know. You can find me over here in this method ChangeHVAC_FanSpeed_Button_Was_Pressed". So when this event gets fired, or if it gets fired, then it will go to ChangeHVAC_FanSpeed_Button_Was_Pressed. That's all that line does. There is a contractual sytnax it has to fulfill by being a FuseGL_FGLO_MouseEvent, but that's it. You may have noticed that we are saying go here when this happens, but we haven't coded this yet. So if the program ran now, where would it go? Nowhere! It would be a bug and crash! So Visual Studio and C# says, "hey idiot, you forgot something" and tells you pretty explicitly what it is:


    Notice how it tells you the first error is that "The name 'ChangeHVAC_FanSpeed_Button_Was_Pressed' does not exist in the current context" and it gives you the file name and the line number and the position the error is in. Pretty sweet huh?

    The last thing we do in that setup routine is call this method called ChangeFanSpeed that also doesn't exist. But we are going to pass in the FuseGL Object to draw the picture update and also what fan speed to change it to. This method doesn't exist yet, but we are going to call it eventually. That should be your second error.

    Code:
                // Change the image to something known to begin with
                ChangeFanSpeed(currentFanSpeed, displayFuseGL_Object_for_fan_speed);


    So now we just need to make the two routines that don't exist, and fix the 2 errors.

    The first is the event that we hooked into:

    Code:
            void  ChangeHVAC_FanSpeed_Button_Was_Pressed(object sender, FuseGL.FuseGL_GUI.MouseEventInformation MouseInfo)
            {
                switch(currentFanSpeed)
                {
                    case FanSpeed.OFF:
                        currentFanSpeed = FanSpeed.LOW;
                        break;
                    case FanSpeed.LOW:
                        currentFanSpeed = FanSpeed.MEDIUM;
                        break;
                    case FanSpeed.MEDIUM:
                        currentFanSpeed = FanSpeed.HIGH;
                        break;
                    case FanSpeed.HIGH:
                        currentFanSpeed = FanSpeed.OFF;
                        break;
                }
    
                ChangeFanSpeed(currentFanSpeed, (FuseGL.GUI.FuseGL_Object)sender);
            }
    It does exactly what it should do. When you press the button what do you want to happen? Change the fan speed right? And since we are doing a single rotating button, you need someway of walking from OFF->LOW->MEDIUM->HIGH and then back to OFF. That is exactly what the switch statement does. The switch is like an if. It "switches" on the variable in its "()" area which is the current fan speed variable. So if that variable is OFF, then go to LOW. If it is LOW, go to MEDIUM. If it is MEDIUM, go to HIGH. If it is HIGH, go to OFF. Or maybe you want it to walk up and down, you can do that too. Anything you want.

    The other part is calling this same method that doesn't exist ChangeFanSpeed. We are passing into it the current fan speed (currentFanSpeed variable) but also the FGLO (FuseGL Object) that we want to change the image of, just like in the setup function. Except we are doing it now based on the sender. You may have noticed that the ChangeHVAC_FanSpeed_Button_Was_Pressed method had two input parameters. One was the sender, the other was some Mouse info like what the pointer position was and so on, but we don't need that. Even though we don't need it, we can choose not to use it. But we do need to give it a slot in our definition of the method. The object sender part is what is interesting. Object is a generic term, just like in English. Instead of saying we need one of these very specific things, we are saying it is some object, any object. Now we know it is a FGLO, but it could really be anything. So when we pass it into the ChangeFanSpeed method, we need to take from being anything to being something specific again. This is what casting is for. Yes the potato is a thing. But it is also a vegetable. Some buckets may take any thing, like a grocery basket. Some buckets only take vegetables, like recipes in the vegetable section of a cookbook. Some only take potatoes, like a mashed potato recipe. So the grocery store is the generic "object" bucket, it can take anything. But you know that the generic object is a potato and you are passing it into a mashed potato recipe so you are calling the thing you pull out a potato. You are not making chocolate pudding with it. So you cast it to a FuseGL.GUI.FuseGL_Object by wrapping that in parens and putting it before the statement:

    Code:
    ChangeFanSpeed(currentFanSpeed, (FuseGL.GUI.FuseGL_Object)sender);
    Call the method ChangeFanSpeed, and pass in the currentFanSpeed and the FuseGL_Object in the variable sender.

    So the only thing left is the ChangeFanSpeed method:

    Code:
            public void ChangeFanSpeed(FanSpeed _speed, FuseGL.GUI.FuseGL_Object fanSpeedFGLO = null)
            {
                // Put some info in the trace for debugging perhaps?
                FuseGL.FuseGL_Debugger.Trace.Add("Changing Fan Speed to: " + _speed);
    
                // First turn off all the outputs
                foreach(KeyValuePair<FanSpeed, MyInternalHVAC_FanSpeed_Class> aFanSpeed in FanSpeedLookupDictionary)
                {
                    aFanSpeed.Value.DigitalOutputPort.Value = FB_Monitor.DigitalOutputPort.DIGITAL_OUTPUT_OFF;
                }
    
                // Then if it is a valid fan speed and the dictionary has been setup properly...
                if(FanSpeedLookupDictionary.ContainsKey(_speed))
                {
                    // Turn that output on
                    FanSpeedLookupDictionary[_speed].DigitalOutputPort.Value = FB_Monitor.DigitalOutputPort.DIGITAL_OUTPUT_ON;
    
                    // If the FGLO to display a picture in is valid...
                    if (fanSpeedFGLO != null)
                    {
                        // Change the texture to the correct one depicting the new fan speed
                        fanSpeedFGLO.TextureIsBeingOverriddenAndControlledExternally = true;
                        fanSpeedFGLO.TextureToOverrideWith = FanSpeedLookupDictionary[_speed].FGLO_Texture;
                    }
                }
                return;
            }
    Let's break it down again.

    First we call FuseGL.FuseGL_Debugger.Trace.Add(string something) and that puts the "something" part of it into FuseGL_Trace.txt. You can make it say whatever you want. It is not needed, but if something is not working or you want to see if it is ever getting there, you can include this. You can use it anywhere in your code. But be careful, if it is everywhere you may get a very large logfile!

    Code:
    FuseGL.FuseGL_Debugger.Trace.Add(...);
    Next we turn off all of the outputs. We don't want the fan to be on HIGH and MEDIUM at the same time. We first want the fan to go off momentarily, and then go to the next setting. That way you don't short anything!

    Code:
                // First turn off all the outputs
                foreach(KeyValuePair<FanSpeed, MyInternalHVAC_FanSpeed_Class> aFanSpeed in FanSpeedLookupDictionary)
                {
                    aFanSpeed.Value.DigitalOutputPort.Value = FB_Monitor.DigitalOutputPort.DIGITAL_OUTPUT_OFF;
                }
    This says, go through everything in the dictionary (we added to it in the setup function), lookup the digital output port it is connected to, and turn it off.

    Then this is the meat:

    Code:
                // Then if it is a valid fan speed and the dictionary has been setup properly...
                if(FanSpeedLookupDictionary.ContainsKey(_speed))
                {
                    // Turn that output on
                    FanSpeedLookupDictionary[_speed].DigitalOutputPort.Value = FB_Monitor.DigitalOutputPort.DIGITAL_OUTPUT_ON;
    
                    // If the FGLO to display a picture in is valid...
                    if (fanSpeedFGLO != null)
                    {
                        // Change the texture to the correct one depicting the new fan speed
                        fanSpeedFGLO.TextureIsBeingOverriddenAndControlledExternally = true;
                        fanSpeedFGLO.TextureToOverrideWith = FanSpeedLookupDictionary[_speed].FGLO_Texture;
                    }
                }
    If the _speed variable (which astute readers will see is what we are now calling what we passed in as currentFanSpeed) is in the dictionary
    Code:
    if(FanSpeedLookupDictionary.ContainsKey(_speed))
    ... then lookup the MyInternalHVAC_FanSpeed_Class that relates to this speed...

    Code:
    FanSpeedLookupDictionary[_speed]
    ... and get the DigitalOutputPort object...

    Code:
    FanSpeedLookupDictionary[_speed].DigitalOutputPort
    ... and get the Value of the DigitalOutputPort...

    Code:
    FanSpeedLookupDictionary[_speed].DigitalOutputPort.Value
    ... and set it equal to...

    Code:
    FanSpeedLookupDictionary[_speed].DigitalOutputPort.Value =
    ... the value of FB_Monitor.DigitalOutputPort.DIGITAL_OUTPUT_ON which is a constant that just means turn on.

    Code:
    FanSpeedLookupDictionary[_speed].DigitalOutputPort.Value = FB_Monitor.DigitalOutputPort.DIGITAL_OUTPUT_ON;
    Then after that, we check if the FuseGL Object exists (is not null, "!=" is "not equal to" and "null" means doesn't exist).

    Code:
     
    if (fanSpeedFGLO != null)
    ... so if it does exist, then set the FuseGL Object's property that says you are taking control of drawing the image to true, FuseGL will now back off and not change images depending on hovering or clicking or anything:

    Code:
                        fanSpeedFGLO.TextureIsBeingOverriddenAndControlledExternally = true;
    ... and then set the texture to draw equal to the value of the texture stored in the value of the lookup dictionary that relates to _speed

    Code:
                        fanSpeedFGLO.TextureToOverrideWith = FanSpeedLookupDictionary[_speed].FGLO_Texture;

    So that's all for the coding in Visual Studio. Go to Build and make sure you have no errors and you get Build Succeeded (instructions above).

    Full Code:

    Code:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using FB_USB_2011;
    using FuseGL.GUI;
    
    namespace HVAC_Control_Example
    {
        public class HVAC_Example
        {
            public enum FanSpeed
            {
                OFF,
                LOW,
                MEDIUM,
                HIGH
            }
    
            internal class MyInternalHVAC_FanSpeed_Class
            {
                public FB_Monitor.DigitalOutputPort DigitalOutputPort;
                public FuseGL.GUI.FGLO_Texture FGLO_Texture;
    
                public MyInternalHVAC_FanSpeed_Class(FB_Monitor.DigitalOutputPort digitalOutputPort, FGLO_Texture fgloTexture)
                {
                    DigitalOutputPort = digitalOutputPort;
                    FGLO_Texture = fgloTexture;
                }
            }
    
            internal Dictionary<FanSpeed, MyInternalHVAC_FanSpeed_Class> FanSpeedLookupDictionary = new Dictionary<FanSpeed, MyInternalHVAC_FanSpeed_Class>();
            internal FanSpeed currentFanSpeed = FanSpeed.OFF;
    
            public void setup_HVAC(
                                    FuseGL.GUI.FuseGL_Object displayFuseGL_Object_for_fan_speed,
                                    FB_Monitor.DigitalOutputPort _digitalOutputPort_for_fan_off,
                                    FB_Monitor.DigitalOutputPort _digitalOutputPort_for_fan_low,
                                    FB_Monitor.DigitalOutputPort _digitalOutputPort_for_fan_medium,
                                    FB_Monitor.DigitalOutputPort _digitalOutputPort_for_fan_high
                                  )
            {
                // Setup FanSpeed textures and ports
                FanSpeedLookupDictionary.Add(FanSpeed.OFF, new MyInternalHVAC_FanSpeed_Class(_digitalOutputPort_for_fan_off, FGLO_Texture.MakeQuickFGLOTexture___ConvertFromExternalImagePath("HVAC_Fan_Control___FanSpeedImage___OFF", "images/HVAC/FanSpeed/Off.png")));
                FanSpeedLookupDictionary.Add(FanSpeed.LOW, new MyInternalHVAC_FanSpeed_Class(_digitalOutputPort_for_fan_low, FGLO_Texture.MakeQuickFGLOTexture___ConvertFromExternalImagePath("HVAC_Fan_Control___FanSpeedImage___LOW", "images/HVAC/FanSpeed/Low.png")));
                FanSpeedLookupDictionary.Add(FanSpeed.MEDIUM, new MyInternalHVAC_FanSpeed_Class(_digitalOutputPort_for_fan_medium, FGLO_Texture.MakeQuickFGLOTexture___ConvertFromExternalImagePath("HVAC_Fan_Control___FanSpeedImage___MEDIUM", "images/HVAC/FanSpeed/Medium.png")));
                FanSpeedLookupDictionary.Add(FanSpeed.HIGH, new MyInternalHVAC_FanSpeed_Class(_digitalOutputPort_for_fan_high, FGLO_Texture.MakeQuickFGLOTexture___ConvertFromExternalImagePath("HVAC_Fan_Control___FanSpeedImage___HIGH", "images/HVAC/FanSpeed/High.png")));
    
                // Hook into when the FGLO is pressed
                displayFuseGL_Object_for_fan_speed.MouseEvent___ClickDown += new FuseGL_Object.FuseGL_FGLO_MouseEvent(ChangeHVAC_FanSpeed_Button_Was_Pressed);
    
                // Change the image to something known to begin with
                ChangeFanSpeed(currentFanSpeed, displayFuseGL_Object_for_fan_speed);
            }
        }
    }
    Now we need to tell FuseGL to look for this file, use it, and we need some place to draw the textures which would be that FuseGL Object we have been talking about. So open your GUI XML file. If you have never run FuseGL, run it once. It will spit out some folders and files.

    So never run before:


    And after 1 run:

    Then select the UserGUI configuration file which is an XML called "UserGUIConfig.xml"


    Again you can open it in Notepad if you wanted to, but you should probably open it in Visual Studio to make it easy. So go to File->Open from the top menu


    If it is your first edits, it will look like this:
    Code:
    <?xml version="1.0" encoding="utf-8"?>
    <FuseGL_User_Settings>
    </FuseGL_User_Settings>


    If you have a lot stuff in there already, it may look like:
    Code:
    <?xml version="1.0" encoding="utf-8"?>
    <FuseGL_User_Settings>
      <Persistant_Variables>
        <variable name="autoVariable___[Sensor - Temperature in Celcius[0,1] - OUTSIDE TEMPERATURE]" first_value="0.0" type="double" />
      </Persistant_Variables>
      <FusionBrainOnline>
        <Login_Details user_name="" password="" />
        <Status enabled="true" />
      </FusionBrainOnline>
      <User_Object type="FuseGL_Object">
        <setting type="string" name="GL - LBL - Sensor - Temperature in Celcius[0,1] - OUTSIDE TEMPERATURE" />
        <image location="internal" path="FuseGL.Configurator.Configurator_IMG_Resources.UserButtonBackground" />
        <text text="(action){FuseGL.ExecuteCode(UpdateLabelText.cs;FuseGL_TextManipulation.InternalFlyCompile_UpdateLabelText;ChangeButtonTextOnVariablePersistant;{$[*].autoVariable___[Sensor - Temperature in Celcius[0,1] - OUTSIDE TEMPERATURE]};{this};0.000;C;)}" />
        <size width="210" height="30" />
        <location x="30" y="60" alignment="left" z="0.0" />
        <setting type="int" border_size="1" />
        <setting type="bool" mouse_cursor_to_hand_on_hover="false" />
        <events>
          <startup action="FuseGL.ExecuteCode" inputArgs="Sensors.cs;FuseGL_Sensors.Sensors;Initialize___TempSensor___Celcius;{FB.AnalogueInput(0.1)};{$[*].autoVariable___[Sensor - Temperature in Celcius[0,1] - OUTSIDE TEMPERATURE]};" outputTo="this.text.text" />
        </events>
      </User_Object>
      <User_Object type="FuseGL_Object">
        <setting type="string" name="GL - BTN - DO[0,0]" />
        <image location="internal" path="FuseGL.Configurator.Configurator_IMG_Resources.UserButtonBackground" />
        <image_hover location="internal" path="FuseGL.Configurator.Configurator_IMG_Resources.BaseButtonHover" />
        <text text="Toggle: OUTPUT 0" />
        <size width="210" height="30" />
        <location x="30" y="105" z="0" alignment="L" />
        <setting type="int" border_size="1" />
        <setting type="bool" mouse_cursor_to_hand_on_hover="true" />
        <events>
          <startup action="FuseGL.ExecuteCode" inputArgs="ChangeTextOnDigitalOutputChange.cs;FuseGL_TextManipulation.InternalFlyCompile_ChangeTextOnDigitalOutputChange;ChangeButtonTextOnDigitalOutputPersistant;{FB.DigitalOutput(0.0)};{this.text};Turn off: OUTPUT 0;Turn on: OUTPUT 0;" outputTo="this.text.text" />
          <onMouseDown action="FB.ToggleDigitalOutput" inputArgs="Port:0.0" />
        </events>
      </User_Object>
      <User_Object type="FuseGL_Object">
        <setting type="string" name="GL - BTN - DO[0,1]" />
        <image location="internal" path="FuseGL.Configurator.Configurator_IMG_Resources.UserButtonBackground" />
        <image_hover location="internal" path="FuseGL.Configurator.Configurator_IMG_Resources.BaseButtonHover" />
        <text text="Toggle: OUTPUT 1" />
        <size width="210" height="30" />
        <location x="30" y="150" alignment="left" z="0.0" />
        <setting type="int" border_size="1" />
        <setting type="bool" mouse_cursor_to_hand_on_hover="true" />
        <events>
          <startup action="FuseGL.ExecuteCode" inputArgs="ChangeTextOnDigitalOutputChange.cs;FuseGL_TextManipulation.InternalFlyCompile_ChangeTextOnDigitalOutputChange;ChangeButtonTextOnDigitalOutputPersistant;{FB.DigitalOutput(0.1)};{this.text};Turn off: OUTPUT 1;Turn on: OUTPUT 1;" outputTo="this.text.text" />
          <onMouseDown action="FB.ToggleDigitalOutput" inputArgs="Port:0.1" />
        </events>
      </User_Object>
    </FuseGL_User_Settings>
    We need to add a node somewhere in there. It needs to be a child of the root FuseGL_User_Settings node which means inbetween the <FuseGL_User_Settings> and </FuseGL_User_Settings> tags. Cope and paste this in there:

    Code:
      <User_Object type="FuseGL_Object">
        <setting type="string" name="GL - HVAC CONTROL - FAN SPEED INDICATOR" />
        <image location="internal" path="NULL" />
        <size width="200" height="200" />
        <location x="300" y="50" alignment="left" z="0.0" />
        <setting type="bool" mouse_cursor_to_hand_on_hover="true" />
        <events>
          <startup action="FuseGL.ExecuteCode" inputArgs="HVAC_Example.cs;HVAC_Control_Example.HVAC_Example;setup_HVAC;{this};{FB.DigitalOutput(0.1)};{FB.DigitalOutput(0.2)};{FB.DigitalOutput(0.3)};{FB.DigitalOutput(0.4)};" />
        </events>
      </User_Object>
    This is a lot to digest. Important highlights are setting the name to GL - HVAC CONTROL - FAN SPEED INDICATOR, a null internal image to begin with, then a 200px by 200px located 300px from the left and 50px from the top at a Z index of 0.0. And when the mouser hovers over it, change the cursor to a hand so you know it is clickable. Then the events have some more important things. The startup action is what calls our setup function, and that's all that's needed. Because remember after we call this setup function the hooks into FuseGL are in place. Then FuseGL can poke your code when something important happens and only when something important happens like the user clicks the FuseGL Object. You will notice that this object type is a FuseGL Object! What a coincidence! So the meat of the line is here:

    Code:
    <startup action="FuseGL.ExecuteCode" inputArgs="HVAC_Example.cs;HVAC_Control_Example.HVAC_Example;setup_HVAC;{this};{FB.DigitalOutput(0.1)};{FB.DigitalOutput(0.2)};{FB.DigitalOutput(0.3)};{FB.DigitalOutput(0.4)};" />
    The action is FuseGL.ExecuteCode which means execute code outside of FuseGL somewhere. What do you want to execute? Well it is in the file HVAC_Example.cs. Then the namespace HVAC_Control_Example, the class HVAC_Example and then the method setup_HVAC

    File:
    Code:
    <startup action="FuseGL.ExecuteCode" inputArgs="HVAC_Example.cs;HVAC_Control_Example.HVAC_Example;setup_HVAC;{this};{FB.DigitalOutput(0.1)};{FB.DigitalOutput(0.2)};{FB.DigitalOutput(0.3)};{FB.DigitalOutput(0.4)};" />


    Namespace:
    Code:
    <startup action="FuseGL.ExecuteCode" inputArgs="HVAC_Example.cs;HVAC_Control_Example.HVAC_Example;setup_HVAC;{this};{FB.DigitalOutput(0.1)};{FB.DigitalOutput(0.2)};{FB.DigitalOutput(0.3)};{FB.DigitalOutput(0.4)};" />


    Class:
    Code:
    <startup action="FuseGL.ExecuteCode" inputArgs="HVAC_Example.cs;HVAC_Control_Example.HVAC_Example;setup_HVAC;{this};{FB.DigitalOutput(0.1)};{FB.DigitalOutput(0.2)};{FB.DigitalOutput(0.3)};{FB.DigitalOutput(0.4)};" />


    Method:
    Code:
    <startup action="FuseGL.ExecuteCode" inputArgs="HVAC_Example.cs;HVAC_Control_Example.HVAC_Example;setup_HVAC;{this};{FB.DigitalOutput(0.1)};{FB.DigitalOutput(0.2)};{FB.DigitalOutput(0.3)};{FB.DigitalOutput(0.4)};" />


    Then you pass in the arguments that the Method needs.

    {this} is the FuseGL Object itself which magically happens to be what we need for our method!
    Code:
    <startup action="FuseGL.ExecuteCode" inputArgs="HVAC_Example.cs;HVAC_Control_Example.HVAC_Example;setup_HVAC;{this};{FB.DigitalOutput(0.1)};{FB.DigitalOutput(0.2)};{FB.DigitalOutput(0.3)};{FB.DigitalOutput(0.4)};" />


    Then the 4 digital outputs, on Fusion Brain Index 0 and Ports 1 (off), 2 (low), 3 (medium), and 4 (high).
    Code:
    <startup action="FuseGL.ExecuteCode" inputArgs="HVAC_Example.cs;HVAC_Control_Example.HVAC_Example;setup_HVAC;{this};{FB.DigitalOutput(0.1)};{FB.DigitalOutput(0.2)};{FB.DigitalOutput(0.3)};{FB.DigitalOutput(0.4)};" />


    Make sure to save the XML file!

    Now all you need is to put the files in the right spot. We need the HVAC_Example.cs file and then the 4 images for the fan states.

    Off: http://www.fusioncontrolcentre.com/F...xample/Off.png
    Low: http://www.fusioncontrolcentre.com/F...xample/Low.png
    Medium: http://www.fusioncontrolcentre.com/F...ple/Medium.png
    High: http://www.fusioncontrolcentre.com/F...ample/High.png

    So put those in your FuseGL directory as we coded in the cs file:


    Then copy the cs file itself:


    Now double click and run the EXE!


    You will see this:


    And if you click it, you will go through the 4 states. If you have a fusion brain connected you will see it cycling through ports 1/2/3/4 each time you click it!






    FIN!

    That wasn't so hard now was it?! There is a lot of info in here. Feel free to play around. The worst case is that it doesn't work. As questions here if you need to!
    Fusion Brain Version 6 Released!
    1.9in x 2.9in -- 47mm x 73mm
    30 Digital Outputs -- Directly drive a relay
    15 Analogue Inputs -- Read sensors like temperature, light, distance, acceleration, and more
    Buy now in the MP3Car.com Store

  3. #3
    Variable Bitrate Grrrmachine's Avatar
    Join Date
    Sep 2005
    Location
    Warsaw, Poland
    Posts
    346
    Just a vote of support for this fantastic document. Without it, I'd never have realised how easy it was to go into Sensors.cs and fiddle around to make my own custom inputs. I've now got the Brain communicating with a standard BMW temp sensor using the original car loom, and working great And all it took was:

    double temperature_in_C = (raw_analogueValue * -29.0) + 73.379;


    For a complete non-coder like me, that's really really confidence-boosting.

  4. #4
    Fusion Brain Creator 2k1Toaster's Avatar
    Join Date
    Mar 2006
    Location
    Colorado, but Canadian!
    Posts
    10,052
    Quote Originally Posted by Grrrmachine View Post
    Just a vote of support for this fantastic document. Without it, I'd never have realised how easy it was to go into Sensors.cs and fiddle around to make my own custom inputs. I've now got the Brain communicating with a standard BMW temp sensor using the original car loom, and working great And all it took was:

    double temperature_in_C = (raw_analogueValue * -29.0) + 73.379;


    For a complete non-coder like me, that's really really confidence-boosting.
    Very great news!

    Hopefully you can start to see the possibilities that FuseGL gives you and that anything is available with pretty easy coding. I am here for any questions you may have.

    I am interested in the temp sensor. It may be helpful to simply start a thread in the sensors area with your code snippet, the type of sensor you are using, and maybe a why. It might help someone else in the future, or at the least be a good reminder 10 years from now when you just can't remember why you did something this way!
    Fusion Brain Version 6 Released!
    1.9in x 2.9in -- 47mm x 73mm
    30 Digital Outputs -- Directly drive a relay
    15 Analogue Inputs -- Read sensors like temperature, light, distance, acceleration, and more
    Buy now in the MP3Car.com Store

  5. #5
    Constant Bitrate
    Join Date
    May 2006
    Posts
    116
    Really nice job!!

    Is there a way to access the format of the http output format to change the XML or even create JSON output format?

  6. #6
    Fusion Brain Creator 2k1Toaster's Avatar
    Join Date
    Mar 2006
    Location
    Colorado, but Canadian!
    Posts
    10,052
    Quote Originally Posted by waltersaegir View Post
    Really nice job!!

    Is there a way to access the format of the http output format to change the XML or even create JSON output format?
    You can do anything you want. Subscribe to the IO events, and then have it output a file or host a service on another port. Your service can output it in whatever format you want, even a completely formatted webpage.
    Fusion Brain Version 6 Released!
    1.9in x 2.9in -- 47mm x 73mm
    30 Digital Outputs -- Directly drive a relay
    15 Analogue Inputs -- Read sensors like temperature, light, distance, acceleration, and more
    Buy now in the MP3Car.com Store

Similar Threads

  1. Customising the look of FuseGL
    By Grrrmachine in forum FB Skins
    Replies: 27
    Last Post: 04-25-2013, 03:32 AM
  2. Variables for FuseGL FB6
    By venfayon in forum FB Software
    Replies: 4
    Last Post: 10-08-2012, 06:11 PM
  3. Plugin starter code and/or documentation
    By EricE in forum RR Plugins
    Replies: 14
    Last Post: 03-24-2012, 12:51 PM
  4. Xenarc 700IDT - questions: Power? Documentation?
    By riptex in forum LCD/Display
    Replies: 1
    Last Post: 12-06-2007, 05:35 PM
  5. Replies: 2
    Last Post: 02-25-2006, 08:34 PM

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •