Tutorial: FLNet Memory Map

This example demonstrates how to load and interact with an FLNet memory map.

First, a memory map description in an XML file is required. Here is a sample memory map that would be called mmap.xml on the server:

<?xml version="1.0" encoding="UTF-8"?>
<watmmap version="1.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="mmap.xsd"> 

<types>
  <udt name="pkToolOut" size="40">
     <cstring name="ASCII" index="2" size="8" />
     <cstring index="12" size="20" name="VIN" />
     <u16 index="0">
        <bool name="Pass" index="0" />
        <bool name="Reject" index="1" />
        <bool name="DataEmpty" index="2" />
        <bool name="DataRequestNG" index="3" />
        <bool name="L10Pulse1Shot" index="4" />
        <bool name="NextVehDataEmpty" index="11" />
        <bool name="DataSTB" index="12" />
        <bool name="L10PulseLevel" index="13" />
        <bool name="CoRunning" index="14" />
        <bool name="HeartBeat" index="15" />
     </u16>

     <u16 index="2">
        <bits name="Tightening1" index="0" size="4" />
        <bits name="Tightening2" index="4" size="4" />
        <bits name="Tightening3" index="8" size="4" />
        <bits name="Tightening4" index="12" size="4" />
     </u16>
     <u16 index="4">
        <bits name="Tightening5" index="0" size="4" />
        <bits name="Tightening6" index="4" size="4" />
        <bits name="Tightening7" index="8" size="4" />
        <bits name="Tightening8" index="12" size="4" />
     </u16>
     <u16 index="6">
        <bits name="Tightening9" index="0" size="4" />
        <bits name="Tightening10" index="4" size="4" />
        <bits name="Tightening11" index="8" size="4" />
        <bits name="Tightening12" index="12" size="4" />
     </u16>
     <u16 index="8">
        <bits name="Tightening13" index="0" size="4" />
        <bits name="Tightening14" index="4" size="4" />
        <bits name="Tightening15" index="8" size="4" />
        <bits name="Tightening16" index="12" size="4" />
     </u16>
     <u16 index="10" name="ASN" />


     <u16 index="34" name="POS" />
     <u16 index="36" name="ToolID" />
     <u16 index="38" name="Spare" />

     <s16 index="32" name="EAP" />
  </udt>
  <udt name="pkToolIn" size="14">
     <u16 index="0">
        <bool name="DataRequest" index="0" />
        <bool name="DataRequestCheck" index="1" />
        <bool name="DataForward" index="2" />
        <bool name="DataBackward" index="3" />
        <bool name="FaultReset" index="4" />
        <bool name="ToolComplete" index="8" />
        <bool name="OnlineMode" index="10" />
        <bool name="InterlockOff" index="11" />
        <bool name="AVIFault" index="12" />
        <bool name="ResetDefaultPos" index="14" />
        <bool name="HeartBeat" index="15" />
     </u16>
     <u16 index="2">
        <bool name="RejectFault" index="1" />
        <bool name="DataEmptyFault" index="2" />
        <bool name="DataRequestFault" index="3" />
        <bool name="NoDataFault" index="8" />
        <bool name="ASNFault" index="9" />
        <bool name="DataSTBError" index="12" />
        <bool name="HeartBeatFault" index="15" />
     </u16>
     <u16 index="4">
        <bool name="FixedStop" index="0" />
        <bool name="TempStop" index="1" />
        <bool name="ForceReset" index="8" />
        <bool name="CrossThread" index="9" />
        <bool name="CountShort" index="10" />
        <bool name="TorqueLow" index="11" />
        <bool name="TorqueHigh" index="12" />
        <bool name="ToolNG" index="13" />
        <bool name="BackupOK" index="14" />
        <bool name="ToolOK" index="15" />
     </u16>
     <u8 name="AndonStopPosition" index="6" />
     <u8 name="LineSide" index="7" />
     <u16 index="8" name="ASN" />
     <u16 index="10" name="ToolID" />
     <u16 index="12" name="Spare" />
  </udt>
  <udt name="pkIn" size="160">
     <pkToolIn name="Tool1" index="0" />
     <pkToolIn name="Tool2" index="14" />
     <pkToolIn name="Tool3" index="28" />
     <pkToolIn name="Tool4" index="42" />
  </udt>
  <udt name="pkOut" size="56">
     <pkToolOut name="Tool1" index="0" />
     <pkToolOut name="Tool2" index="40" />
     <pkToolOut name="Tool3" index="80" />
     <pkToolOut name="Tool4" index="120" />
  </udt>
</types>

<map>
  <!-- no input1 or output1 for this memory map -->
  <input2 type="pkIn" />
  <output2 type="pkOut" />
</map>

</watmmap>

The memory map has quite a few elements in it to describe properties by name and position in the map. It is typically a good idea to use the memory map editor tool to create an XML file, as it helps to identify what would otherwise be difficult to detect errors.

To use the memory map in a WAT application, the map must be loaded from the server, then applied to the FLNet plugin. To load the memory map from the server, the XMLHttpRequest is used:

function getMMap() {
   var xmlhttp = new XMLHttpRequest();
   xmlhttp.open("GET", "mmap.xml", true);
   xmlhttp.onreadystatechange=function() {
   if (xmlhttp.readyState==4) {
      // the memory map, in xmlhttp.responseText, is ready to use
   }
   xmlhttp.send("");
}

The getMMap function should be called after the page has completely loaded. This can be done with the onload attribute of the body tag, or a function provided by a JavaScript framework such as jQuery. Once the map has been loaded, it is applied with flnet.FLSettings#mmap, along with the other FLNet settings:

$flnet.settings.ip.str = '192.168.250.36';
$flnet.settings.a2InStart = 4944;
$flnet.settings.a2InSize = 28; 
$flnet.settings.a2OutStart = 1236;
$flnet.settings.a2OutSize = 80;
$flnet.settings.mmap = mmap;

Finally, FLNet must be enabled using flnet.FLNET#enabled:

$flnet.enabled = true;

Here is the whole example, which displays the contents of the memory map in a table on the screen:

<html>
<head>
<title>Pokayoke</title>
<script type="text/javascript">
var toolin;
var toolout;
var nextasn = 0;
var $flnet;

if (typeof $wat != 'undefined') $flnet = $wat.load('flnet')[0];

if (!$flnet) {
   debugger;
   throw new Error("This example must be run on a WAT device with FLNet");
}

function toggleHeartbeat() {
   toolout.HeartBeat.toggle();
}

function setBit(state) {
   if (state) {
      document.getElementById(this.id).innerHTML=this.text + "<br>1";
      document.getElementById(this.id).style.backgroundColor="green";
   } else {
      document.getElementById(this.id).innerHTML=this.text + "<br>0";
      document.getElementById(this.id).style.backgroundColor="white";
   }
}

function setText(value) {
   document.getElementById(this.id).innerHTML=value;
}

function checkSTB(val) {
   if (val) {
      nextasn = toolin.ASN.v;
   }
}

function toggleOutBit(bit) {
   toolout[bit].toggle();
   toolout[bit].valueChanged(toolout[bit].v);
}

function toggleToolOK() {
   toggleOutBit('ToolComplete');
   toggleOutBit('ToolOK');
}

function shiftASN() {
   toolout.ASN.v = nextasn;
   toolout.ASN.valueChanged(toolout.ASN.v);
}

function initFLNet(mmap) {
   $flnet.settings.ip.str = '192.168.250.36';
   $flnet.settings.a2InStart = 4944;
   $flnet.settings.a2InSize = 28; 
   $flnet.settings.a2OutStart = 1236;
   $flnet.settings.a2OutSize = 80;
   $flnet.settings.mmap = mmap;

   if ($flnet.load() == false) {
      alert("Failed to load FLNet memory map");
      return;
   }

   toolin = $flnet.output2.Tool1;
   toolout = $flnet.input2.Tool1;

   toolin.CoRunning.valueChanged.connect({id: 'corunning', text: 'Co Running'}, setBit);
   toolin.L10PulseLevel.valueChanged.connect({id: 'l10pulsel', text: 'L10 Pulse (Level)'}, setBit);
   toolin.DataSTB.valueChanged.connect({id: 'datastb', text: 'Data STB'}, setBit);
   toolin.DataSTB.valueChanged.connect(checkSTB);
   toolin.NextVehDataEmpty.valueChanged.connect({id: 'nextempty', text: 'Next Veh Data Empty'}, setBit);
   toolin.L10Pulse1Shot.valueChanged.connect({id: 'l10pulse1', text: 'L10 Pulse (1Shot)'}, setBit);
   toolin.DataRequestNG.valueChanged.connect({id: 'requestng', text: 'Data Request NG'}, setBit);
   toolin.DataEmpty.valueChanged.connect({id: 'dataempty', text: 'Data Empty'}, setBit);
   toolin.Reject.valueChanged.connect({id: 'reject', text: 'Reject'}, setBit);
   toolin.Pass.valueChanged.connect({id: 'pass', text: 'Pass'}, setBit);
   for (i = 1; i <= 16; i++) {
      toolin['Tightening' + i].valueChanged.connect({id: 't'+i}, setText);
   }
   toolin.ASN.valueChanged.connect({id: 'asn'}, setText);
   toolin.VIN.valueChanged.connect({id: 'vin'}, setText);
   toolin.EAP.valueChanged.connect({id: 'eap'}, setText);
   toolin.POS.valueChanged.connect({id: 'pos'}, setText);
   toolin.ToolID.valueChanged.connect({id: 'toolid'}, setText);
   toolin.HeartBeat.valueChanged.connect({id: 'heartbeat', text: 'Heart Beat'}, setBit);
   setInterval("toggleHeartbeat()", 1000);
   toolout.AndonStopPosition.v = 1;
   toolout.LineSide.v = 76;
   toolout.ToolID.v = 5555;
   toolout.Spare.v = 257;
   toolout.OnlineMode.set();
   toolout.ToolComplete.valueChanged.connect({id: 'toolcomplete', text: 'Tool Complete'}, setBit);
   toolout.ToolOK.valueChanged.connect({id: 'toolok', text: 'Tool OK'}, setBit);
   toolout.FixedStop.valueChanged.connect({id: 'fixedstop', text: 'Fixed Stop'}, setBit);
   toolout.TempStop.valueChanged.connect({id: 'tempstop', text: 'Temp Stop'}, setBit);
   toolout.DataRequest.valueChanged.connect({id: 'datarequest', text: 'Data Request'}, setBit);
   toolout.DataRequestCheck.valueChanged.connect({id: 'datacheck', text: 'Data Check'}, setBit);
   toolout.FaultReset.valueChanged.connect({id: 'faultreset', text: 'Fault Reset'}, setBit);
   toolout.ASN.valueChanged.connect({id: 'execasn'}, setText);

   $flnet.enabled = true;
}

function getMMap() {
   var xmlhttp = new XMLHttpRequest();
   xmlhttp.open("GET", "mmap.xml", true);
   xmlhttp.onreadystatechange=function() {
   if (xmlhttp.readyState==4) {
      initFLNet(xmlhttp.responseText);
   }
   xmlhttp.send("");
}
</script>

</head>
<body onload="getMMap();">
   <table border=1 align="center">
      <tr>
         <td id="heartbeat">Heart Beat</td>
         <td id="corunning">Co Running</td>
         <td id="l10pulsel">L10 Pulse (Level)</td>
         <td id="datastb">Data STB</td>
         <td id="nextempty">Next Veh Data Empty</td>
         <td>Spare</td>
         <td>Spare</td>
         <td>Spare</td>
         <td>Spare</td>
         <td>Spare</td>
         <td>Spare</td>
         <td id="l10pulse1">L10 Pulse (1Shot)</td>
         <td id="requestng">Data Request NG</td>
         <td id="dataempty">Data Empty</td>
         <td id="reject">Reject</td>
         <td id="pass">Pass</td>
      </tr>
      <tr>
         <td colspan=4 id="t4" align="center">0</td>
         <td colspan=4 id="t3" align="center">0</td>
         <td colspan=4 id="t2" align="center">0</td>
         <td colspan=4 id="t1" align="center">0</td>
      </tr>
      <tr>
         <td colspan=4 id="t8" align="center">0</td>
         <td colspan=4 id="t7" align="center">0</td>
         <td colspan=4 id="t6" align="center">0</td>
         <td colspan=4 id="t5" align="center">0</td>
      </tr>
      <tr>
         <td colspan=4 id="t12" align="center">0</td>
         <td colspan=4 id="t11" align="center">0</td>
         <td colspan=4 id="t10" align="center">0</td>
         <td colspan=4 id="t9" align="center">0</td>
      </tr>
      <tr>
         <td colspan=4 id="t16" align="center">0</td>
         <td colspan=4 id="t15" align="center">0</td>
         <td colspan=4 id="t14" align="center">0</td>
         <td colspan=4 id="t13" align="center">0</td>
      </tr>
      <tr>
         <td colspan=16 id="asn" align="center">0</td>
      </tr>
      <tr>
         <td colspan=16 id="vin" align="center">vin</td>
      </tr>
      <tr>
         <td colspan=16 id="eap" align="center">0</td>
      </tr>
      <tr>
         <td colspan=16 id="pos" align="center">0</td>
      </tr>
      <tr>
         <td colspan=16 id="toolid" align="center">0</td>
      </tr>
   </table>
   <table>
      <tr>
         <td><input type="button"
            onclick="toggleOutBit('FixedStop');" value="Fixed Stop"></td>
         <td id='fixedstop'>Fixed Stop<br>0
         </td>
         <td><input type="button"
            onclick="toggleOutBit('TempStop');" value="Temp Stop"></td>
         <td id='tempstop'>Temp Stop<br>0
         </td>
         <td><input type="button" onclick="toggleToolOK();"
            value="Tool Complete"></td>
         <td id='toolcomplete'>Tool Complete<br>0
         </td>
         <td id='toolok'>Tool OK<br>0
         </td>
      </tr>
      <tr>
         <td><input type="button"
            onclick="toggleOutBit('DataRequest');" value="Data Request"></td>
         <td id='datarequest'>Data Request<br>0
         </td>
         <td><input type="button"
            onclick="toggleOutBit('DataRequestCheck');"
            value="Data Check"></td>
         <td id='datacheck'>Data Check<br>0
         </td>
         <td><input type="button"
            onclick="toggleOutBit('FaultReset');" value="Fault Reset"></td>
         <td id='faultreset'>Fault Reset<br>0
         </td>
      </tr>
      <tr>
         <td><input type="button" onclick="shiftASN();"
            value="Shift ASN"></td>
         <td id='execasn'>0</td>
      </tr>
   </table>
</body>
</html>

One thing worth noting in the example is the special use of the connect calls:

toolin.CoRunning.valueChanged.connect({id: 'corunning', text: 'Co Running'}, setBit);

Instead of simply connecting to a function using connect, a context object is also specified. This object is then used as 'this' when setBit is called. This methodology allows quick reuse of the setBit function with different contexts without having to construct many instances of class, or make many different functions. It is an easy way to pass extra information to the callback as long as the callback is a function (not a method). See Signals for more information.

To try this example, cut and paste the memory map definition into a file called mmap.xml. Then cut and paste the full HTML into a file called mmap.html in the same directory as mmap.xml. Using WATSim, open the mmap.html file using the file: URL notation (e.g. file:///C:/path/to/mmap.html). These two files could also be served by a web server, such as IIS, so they can be used by a WAT device (see 1 - Hello World).