[{"data":1,"prerenderedAt":3464},["ShallowReactive",2],{"node-red-\u002Fnode-red\u002Fprotocol\u002Fopc-ua":3},{"id":4,"title":5,"body":6,"description":3457,"extension":3458,"meta":3459,"navigation":426,"path":3460,"seo":3461,"stem":3462,"__hash__":3463},"nodeRed\u002Fnode-red\u002Fprotocol\u002Fopc-ua.md","Building Secure OPC-UA Server in Node-RED.",{"type":7,"value":8,"toc":3441},"minimark",[9,17,21,30,35,38,42,45,52,74,87,180,183,206,212,222,225,229,232,235,242,257,276,287,293,297,303,310,321,339,349,367,374,384,391,401,408,414,420,436,439,443,452,458,482,508,517,520,527,533,547,550,556,571,574,578,584,593,604,622,625,631,636,642,648,660,663,680,685,688,850,856,976,980,983,990,1115,1118,1273,1277,1280,1286,1297,1446,1449,2593,2597,2600,2693,2696,3016,3019,3023,3026,3032,3035,3044,3051,3054,3060,3079,3083,3086,3096,3110,3120,3127,3141,3144,3158,3168,3178,3181,3184,3195,3201,3224,3228,3231,3238,3248,3255,3270,3281,3291,3299,3302,3310,3317,3331,3338,3345,3348,3355,3361,3388,3391,3394,3401,3407,3410,3419,3422,3429,3432,3437],[10,11,13],"h1",{"id":12},"",[14,15],"binding",{"value":16},"meta.title",[18,19,20],"p",{},"OPC-UA (OPC Unified Architecture) is a communication protocol designed for industrial automation. It enables seamless data exchange and interoperability between various devices, systems, and software applications in the industrial domain. OPC-UA offers secure and reliable communication, making it a preferred choice for building robust industrial solutions. In this document, we will delve into the creation of a fully custom secure OPC-UA Server for PLCs in Node-RED.",[18,22,23,24,29],{},"If you're not familiar with OPC-UA, you can learn more about it ",[25,26,28],"a",{"href":27},"\u002Fblog\u002F2023\u002F07\u002Fhow-to-deploy-a-basic-opc-ua-server-in-node-red\u002F","here",".",[31,32,34],"h2",{"id":33},"introduction","Introduction",[18,36,37],{},"While it's typical to find PLCs that have built-in OPC-UA server capabilities, such as Omron and Siemens, this is not an industry-wide practice. One notable exception is Allen Bradley PLCs. For Allen Bradley, you have to buy FactoryTalk Linx Gateway (formally RSLinx Enterprise) for OPC-UA Server capability, or you need to employ a 3rd party OPC-UA Server. This documentation will guide you through the process of using Node-RED as a 3rd party OPC-UA Server for Allen Bradley, by creating a custom Information Model for the PLC data, publishing it, then securing the server with SSL to make it production-ready.",[31,39,41],{"id":40},"plc-to-opc-ua-server-architecture-overview","PLC to OPC-UA Server Architecture Overview",[18,43,44],{},"A visual representation of our PLC to OPC-UA Server architecture is shown in the drawing below, consisting of 6 major parts.",[18,46,47],{},[48,49],"img",{"alt":50,"src":51},"PLC-Information-Model-1.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002FPLC-Information-Model-1.png",[53,54,55,59,62,65,68,71],"ol",{},[56,57,58],"li",{},"Set up the PLC tags to be sent to the OPC Server",[56,60,61],{},"Read the PLC tags into Node-RED",[56,63,64],{},"Copy the PLC tags into Node-RED context memory",[56,66,67],{},"Program the OPC Server address space",[56,69,70],{},"Encrypt the OPC Server with SSL",[56,72,73],{},"Set up the OPC Client",[18,75,76,77,81,82,86],{},"The PLC is an Allen Bradley, and an instance of Node-RED running on the same OT network as the PLC will act as the OPC UA Server. In our Allen Bradley PLC, we will re-use an example from a ",[25,78,80],{"href":79},"\u002Fblog\u002F2023\u002F06\u002Fnode-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter\u002F","Node-RED as a No-Code EtherNet\u002FIP to S7 Protocol Converter"," where the PLC is simulating a conveyor line, called ",[83,84,85],"em",{},"Line 4 PLC,"," depicted as number 1 architecture drawing above. The tags below represent the data to be transferred from the Line 4 PLC to the Node-RED OPC UA server, depicted as number 2 in the architecture drawing.",[88,89,90,113],"table",{},[91,92,93],"thead",{},[94,95,96,103,108],"tr",{},[97,98,99],"th",{},[100,101,102],"strong",{},"Tag",[97,104,105],{},[100,106,107],{},"Data Type",[97,109,110],{},[100,111,112],{},"Description",[114,115,116,128,138,149,159,170],"tbody",{},[94,117,118,122,125],{},[119,120,121],"td",{},"Conveyor_RTS",[119,123,124],{},"BOOL",[119,126,127],{},"Conveyor Ready to Start",[94,129,130,133,135],{},[119,131,132],{},"Robot_RTS",[119,134,124],{},[119,136,137],{},"Conveyor Robot Ready to Start",[94,139,140,143,146],{},[119,141,142],{},"Robot_Position",[119,144,145],{},"REAL",[119,147,148],{},"Robot Arm position (degrees)",[94,150,151,154,156],{},[119,152,153],{},"Conveyor_Running",[119,155,124],{},[119,157,158],{},"Conveyor is running",[94,160,161,164,167],{},[119,162,163],{},"Line4_State",[119,165,166],{},"DINT",[119,168,169],{},"Line 4 Machine State",[94,171,172,175,177],{},[119,173,174],{},"Line4_Fault",[119,176,124],{},[119,178,179],{},"Line 4 is faulted",[18,181,182],{},"A simple ladder application has been built in the PLC to simulate our conveyor values.",[18,184,185,189,190,194,195,198,199,201,202,205],{},[48,186],{"alt":187,"src":188},"image-20230717-212515.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fimage-20230717-212515.png","\nThe Line 4 PLC tags will be read by Node-RED using an Ethernet\u002FIP driver, with each PLC tag copied to flow context memory as part of an object named ",[191,192,193],"code",{},"conveyorData",", depicted in number 3 of the architecture drawing. Using the ",[191,196,197],{},"node-red-contrib-opcua-server"," node, the ",[191,200,193],{}," object will become part of a hierarchical OPA UA Information Model representing the Line 4 PLC conveyor data, and stored into the ",[83,203,204],{},"OPC UA Server Address Space,"," depicted as number 4 in the architecture drawing. The OPC Server will publish the Line 4 PLC conveyor data, implementing a self-signed SSL certificate to encrypt the OPC traffic and establish a secure connection with an OPC Client application, depicted as number 5 in the architecture drawing.",[207,208,209],"ul",{},[56,210,211],{},"note - if you prefer not to secure the server, you can skip this step and still connect to the server anonymously for testing purposes.",[18,213,214,215,221],{},"The OPC client will be a windows-based ",[25,216,220],{"href":217,"rel":218},"https:\u002F\u002Fwww.prosysopc.com\u002Fproducts\u002Fopc-ua-browser\u002F",[219],"nofollow","Prosys OPC UA Browser",", depicted on the far right as number 6 in our architecture drawing.",[18,223,224],{},"Now that we have laid out a concept for our application, let’s build it.",[31,226,228],{"id":227},"install-custom-nodes","Install Custom Nodes",[18,230,231],{},"First, we need to add three custom nodes that will allow Node-RED to read Ethernet\u002FIP data and add OPC UA Server functionality.",[18,233,234],{},"Click the hamburger icon → manage palette",[18,236,237,241],{},[48,238],{"alt":239,"src":240},"flow-manage-palette.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fflow-manage-palette.png","\n ",[18,243,244,245,248,249,252,253,256],{},"On the ",[191,246,247],{},"install"," tab, search for ",[191,250,251],{},"ethernet"," and install the ",[191,254,255],{},"node-red-contrib-cip-ethernet-ip"," node, which will be used to read the Ethernet\u002FIP fieldbus data from our Allen Bradley PLC.",[18,258,259,263,264,267,268,271,272,275],{},[48,260],{"alt":261,"src":262},"install-eth-ip-node.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Finstall-eth-ip-node.png","\nNext, search for ",[191,265,266],{},"opc"," and install ",[191,269,270],{},"node-red-contrib-opcua"," and ",[191,273,274],{},"node-red-contrib-opc-ua-server",". These nodes take a particularly long time to install, as they require a lot of dependencies. Expect anywhere from 2 to 10 minutes to complete installation, depending on the speed of your system. You will not be able to track the progress of the installation unless you are monitoring the logs on the back-end, so just be patient.",[18,277,278,282,283,286],{},[48,279],{"alt":280,"src":281},"opc-nodes-install.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fopc-nodes-install.png","\nGo to the ",[191,284,285],{},"Nodes"," tab and confirm the 3 custom nodes have been properly installed.",[18,288,289],{},[48,290],{"alt":291,"src":292},"custom-nodes-installed.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fcustom-nodes-installed.png",[31,294,296],{"id":295},"set-up-ethernetip-data","Set Up Ethernet\u002FIP Data",[18,298,299,300,29],{},"Note: this process is largely a recap from the first part of a article where ",[25,301,302],{"href":79},"Node-RED is used as an Ethernet\u002FIP to S7 protocol converter",[18,304,305,306,309],{},"Let’s start by dragging a ",[191,307,308],{},"eth-ip in"," node onto the palette. Then add a new endpoint, which will point to our Line4 PLC.",[18,311,312,316,317,320],{},[48,313],{"alt":314,"src":315},"eth-ip-in-palette.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Feth-ip-in-palette.png","\nIn the endpoint ",[191,318,319],{},"Connection"," properties, the connection information must match the PLC, so set the IP address and CPU slot number appropriately. Also, the default cycle time is 500ms. Depending on your application, polling the CPU at 500ms may be appropriate. But for our OPC UA application, we will change it to 1000ms, which is a more appropriate polling rate for this type of application.",[18,322,323,327,328,331,332,335,336,29],{},[48,324],{"alt":325,"src":326},"ethip-node-connection.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fethip-node-connection.png","\nOn the ",[191,329,330],{},"Tags"," tab, populate the tag information to match our Allen Bradley PLC. Then select ",[191,333,334],{},"Update"," to complete configuration of the ",[191,337,338],{},"eth-ip endpoint",[18,340,341,345,346,348],{},[48,342],{"alt":343,"src":344},"eth-ip-endpoint-tags.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Feth-ip-endpoint-tags.png","\nNow that we have our endpoint, let’s finish configuring the ",[191,347,308],{}," node.",[53,350,351,354,364],{},[56,352,353],{},"select the endpoint we just created",[56,355,356,357,360,361],{},"Change ",[191,358,359],{},"Mode"," To ",[191,362,363],{},"All tags",[56,365,366],{},"Give the node a descriptive name.",[18,368,369,373],{},[48,370],{"alt":371,"src":372},"eth-ip-in-properties.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Feth-ip-in-properties.png","\nAs configured, the node is going to read all PLC tags any time a value is changed. Press done to complete the configuration.",[18,375,376,377,380,381,383],{},"Before we deploy this flow, let’s wire a ",[191,378,379],{},"debug"," node to our ",[191,382,308],{}," node to confirm Node-RED can read the tags from our PLC.",[18,385,386,390],{},[48,387],{"alt":388,"src":389},"eth-ip-debug.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Feth-ip-debug.png","\nDeploy the flow.",[18,392,393,397,398,400],{},[48,394],{"alt":395,"src":396},"deploy-flow.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fdeploy-flow.png","\nClick the ",[191,399,379],{}," tab and confirm data is flowing in from our PLC.",[18,402,403,407],{},[48,404],{"alt":405,"src":406},"debug-data.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fdebug-data.png","\nWe can see that all tags are being read from the PLC in one message as a key\u002Fvalue hash table, or dict.",[18,409,410,411,413],{},"After confirming the PLC data acquisition is working, we can remove the ",[191,412,379],{}," node and continue building the rest of our flow. Referring back to our architecture drawing, we’ve now taken care of the first 2 objectives of our application.",[18,415,416],{},[48,417],{"alt":418,"src":419},"PLC-Information-Model-2-of-6-1.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002FPLC-Information-Model-2-of-6-1.png",[18,421,422,427,428,431,433,434],{},[423,424],"input",{"type":425,"checked":426},"checkbox",true," Set up the PLC tags to be sent to the OPC Server",[429,430],"br",{},[423,432],{"type":425,"checked":426}," Read the PLC tags into Node-RED",[429,435],{},[18,437,438],{},"Let’s move on to objective 3.",[31,440,442],{"id":441},"store-the-plc-data-in-flow-context-memory","Store the PLC Data In Flow Context Memory",[18,444,445,446,449,450,348],{},"Drag a ",[191,447,448],{},"change"," node onto the palette and wire it to the ",[191,451,308],{},[18,453,454],{},[48,455],{"alt":456,"src":457},"change-node-palette.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fchange-node-palette.png",[18,459,460,461,464,465,468,469,471,472,474,475,477,478],{},"We’re going move the data from the PLC into flow context memory, by setting each element of the outgoing ",[191,462,463],{},"msg.payload"," to ",[191,466,467],{},"flow.conveyorData",". To do this, refer back to the structure of the ",[191,470,463],{}," from the ",[191,473,379],{}," node we connected to the ",[191,476,308],{}," node earlier -\n",[48,479],{"alt":480,"src":481},"msg-payload.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fmsg-payload.png",[18,483,484,485,488,489,491,492,495,496,499,500,503,504],{},"Now open up the change node, and press the ",[191,486,487],{},"+add"," button to add a rule for each PLC tag in our ",[191,490,463],{}," object (6), and ",[191,493,494],{},"set"," each rule so that you're setting a ",[191,497,498],{},"flow"," value to a ",[191,501,502],{},"msg"," value.  Then populate each rule as shown -\n",[48,505],{"alt":506,"src":507},"change-node-properties.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fchange-node-properties.png",[18,509,510,511,513,514,516],{},"We've now configured the ",[191,512,448],{}," node to move the data from our PLC into a dict called ",[191,515,193],{},", stored in flow context memory.",[18,518,519],{},"Give the node an appripriate name, hit done and deploy the flow.",[18,521,522,523],{},"Our flow should now look like below -\n",[48,524],{"alt":525,"src":526},"flow-with-change-palette.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fflow-with-change-palette.png",[18,528,529,530,532],{},"Let’s look at the flow context memory to confirm the data from our PLC is being written to the ",[191,531,193],{}," object we created.",[18,534,535,539,543,544,546],{},[48,536],{"alt":537,"src":538},"context-data-1.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fcontext-data-1.png",[48,540],{"alt":541,"src":542},"context-refresh.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fcontext-refresh.png","\nEvery time we hit refresh, the values in ",[191,545,193],{}," change as the value in the PLC changes, confirming things are working as expected.",[18,548,549],{},"Looking back at the application architecture we laid out, we’ve achieved 3 out of the 6 objectives.",[18,551,552],{},[48,553],{"alt":554,"src":555},"PLC-Information-Model-3-of-6-1.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002FPLC-Information-Model-3-of-6-1.png",[18,557,558,427,560,562,433,564,566,568,569],{},[423,559],{"type":425,"checked":426},[429,561],{},[423,563],{"type":425,"checked":426},[429,565],{},[423,567],{"type":425,"checked":426}," Copy the PLC tags into Node-RED context memory",[429,570],{},[18,572,573],{},"Let’s now tackle the OPC Server address space.",[31,575,577],{"id":576},"program-the-opc-ua-server-address-space","Program the OPC UA Server Address Space",[18,579,580,581,29],{},"To make our lives significantly easier, we’re going to start from a template, the same template used in ",[25,582,583],{"href":27},"part 1 of our OPC UA Series",[18,585,586,587,592],{},"Copy the content of the ",[25,588,591],{"href":589,"rel":590},"https:\u002F\u002Fgithub.com\u002FBiancoRoyal\u002Fnode-red-contrib-opcua-server\u002Fblob\u002Fmaster\u002Fexamples\u002Fserver-with-context.json",[219],"example template",", then paste it into Node-RED to import it.",[18,594,595,599,603],{},[48,596],{"alt":597,"src":598},"import.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fimport.png",[48,600],{"alt":601,"src":602},"import-context.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fimport-context.png","\nYou end up with a new flow that looks like the one below.",[18,605,606,610,611,614,615,618,619,621],{},[48,607],{"alt":608,"src":609},"example-flow.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fexample-flow.png","\nAll we care about here is the ",[191,612,613],{},"Compact-Server"," node.  In fact, we’ll just copy that node and paste it into the current flow we’ve been building.  Once we’ve copied the server node into our custom flow, we can discard the example flow.  The whole purpose of this was to simply populate the ",[191,616,617],{},"address space"," of the ",[191,620,613],{}," node with template code that will trivialize the programming for our custom application.",[18,623,624],{},"Our custom flow should now look something like this.",[18,626,627],{},[48,628],{"alt":629,"src":630},"flow-with-compact-server.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fflow-with-compact-server.png",[207,632,633],{},[56,634,635],{},"note - I’ve added some comments to make the flow even easier to follow.  Similar to commenting code, commenting flows is good practice.",[18,637,638,639,641],{},"Open up the ",[191,640,613],{}," node and jump straight to the address space.",[18,643,644],{},[48,645],{"alt":646,"src":647},"compact-server-node-address-space.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fcompact-server-node-address-space.png",[207,649,650],{},[56,651,652,653,655,656,659],{},"note - we won’t go into detail on what the address space actually is in this documentation, or the details of the ",[191,654,613],{}," node, as it was covered in ",[25,657,658],{"href":27},"part 1 of this OPC UA series",".  Please read that documentation if you are unfamiliar with it.",[18,661,662],{},"There are 4 key things we’ll modify in the address space template code -",[53,664,665,671,674,677],{},[56,666,667,668,670],{},"Bring in our ",[191,669,193],{}," context variables",[56,672,673],{},"create our custom folder structure",[56,675,676],{},"define our context variables as OPC UA nodes",[56,678,679],{},"create custom browser views for our nodes",[681,682,684],"h3",{"id":683},"bring-in-context-variables","Bring in Context Variables",[18,686,687],{},"Starting from the section of code where it’s bringing in the context variables defined in the example, delete that code",[689,690,694],"pre",{"className":691,"code":692,"language":693,"meta":12,"style":12},"language-javascript shiki shiki-themes github-light github-dark","  this.sandboxFlowContext.set(\"isoInput1\", 0);\n  this.setInterval(() => {\n    flexServerInternals.sandboxFlowContext.set(\n      \"isoInput1\",\n      Math.random() + 50.0\n    );\n  }, 500);\n  this.sandboxFlowContext.set(\"isoInput2\", 0);\n ...\n this.sandboxFlowContext.set(\"isoOutput8\", 0);\n","javascript",[191,695,696,728,748,759,768,786,792,803,823,829],{"__ignoreMap":12},[697,698,701,705,709,712,715,719,722,725],"span",{"class":699,"line":700},"line",1,[697,702,704],{"class":703},"sj4cs","  this",[697,706,708],{"class":707},"sVt8B",".sandboxFlowContext.",[697,710,494],{"class":711},"sScJk",[697,713,714],{"class":707},"(",[697,716,718],{"class":717},"sZZnC","\"isoInput1\"",[697,720,721],{"class":707},", ",[697,723,724],{"class":703},"0",[697,726,727],{"class":707},");\n",[697,729,731,733,735,738,741,745],{"class":699,"line":730},2,[697,732,704],{"class":703},[697,734,29],{"class":707},[697,736,737],{"class":711},"setInterval",[697,739,740],{"class":707},"(() ",[697,742,744],{"class":743},"szBVR","=>",[697,746,747],{"class":707}," {\n",[697,749,751,754,756],{"class":699,"line":750},3,[697,752,753],{"class":707},"    flexServerInternals.sandboxFlowContext.",[697,755,494],{"class":711},[697,757,758],{"class":707},"(\n",[697,760,762,765],{"class":699,"line":761},4,[697,763,764],{"class":717},"      \"isoInput1\"",[697,766,767],{"class":707},",\n",[697,769,771,774,777,780,783],{"class":699,"line":770},5,[697,772,773],{"class":707},"      Math.",[697,775,776],{"class":711},"random",[697,778,779],{"class":707},"() ",[697,781,782],{"class":743},"+",[697,784,785],{"class":703}," 50.0\n",[697,787,789],{"class":699,"line":788},6,[697,790,791],{"class":707},"    );\n",[697,793,795,798,801],{"class":699,"line":794},7,[697,796,797],{"class":707},"  }, ",[697,799,800],{"class":703},"500",[697,802,727],{"class":707},[697,804,806,808,810,812,814,817,819,821],{"class":699,"line":805},8,[697,807,704],{"class":703},[697,809,708],{"class":707},[697,811,494],{"class":711},[697,813,714],{"class":707},[697,815,816],{"class":717},"\"isoInput2\"",[697,818,721],{"class":707},[697,820,724],{"class":703},[697,822,727],{"class":707},[697,824,826],{"class":699,"line":825},9,[697,827,828],{"class":743}," ...\n",[697,830,832,835,837,839,841,844,846,848],{"class":699,"line":831},10,[697,833,834],{"class":703}," this",[697,836,708],{"class":707},[697,838,494],{"class":711},[697,840,714],{"class":707},[697,842,843],{"class":717},"\"isoOutput8\"",[697,845,721],{"class":707},[697,847,724],{"class":703},[697,849,727],{"class":707},[18,851,852,853,855],{},"and replace it with our ",[191,854,193],{}," context variables.",[689,857,859],{"className":691,"code":858,"language":693,"meta":12,"style":12},"  this.sandboxFlowContext.set(\"conveyorData.Conveyor_RTS\", false);\n  this.sandboxFlowContext.set(\"conveyorData.Robot_RTS\", false);\n  this.sandboxFlowContext.set(\"conveyorData.Robot_Position\", 0);\n  this.sandboxFlowContext.set(\"conveyorData.Conveyor_Running\", false);\n  this.sandboxFlowContext.set(\"conveyorData.Line4_State\", 0);\n  this.sandboxFlowContext.set(\"conveyorData.Line4_Fault\", false);\n",[191,860,861,881,900,919,938,957],{"__ignoreMap":12},[697,862,863,865,867,869,871,874,876,879],{"class":699,"line":700},[697,864,704],{"class":703},[697,866,708],{"class":707},[697,868,494],{"class":711},[697,870,714],{"class":707},[697,872,873],{"class":717},"\"conveyorData.Conveyor_RTS\"",[697,875,721],{"class":707},[697,877,878],{"class":703},"false",[697,880,727],{"class":707},[697,882,883,885,887,889,891,894,896,898],{"class":699,"line":730},[697,884,704],{"class":703},[697,886,708],{"class":707},[697,888,494],{"class":711},[697,890,714],{"class":707},[697,892,893],{"class":717},"\"conveyorData.Robot_RTS\"",[697,895,721],{"class":707},[697,897,878],{"class":703},[697,899,727],{"class":707},[697,901,902,904,906,908,910,913,915,917],{"class":699,"line":750},[697,903,704],{"class":703},[697,905,708],{"class":707},[697,907,494],{"class":711},[697,909,714],{"class":707},[697,911,912],{"class":717},"\"conveyorData.Robot_Position\"",[697,914,721],{"class":707},[697,916,724],{"class":703},[697,918,727],{"class":707},[697,920,921,923,925,927,929,932,934,936],{"class":699,"line":761},[697,922,704],{"class":703},[697,924,708],{"class":707},[697,926,494],{"class":711},[697,928,714],{"class":707},[697,930,931],{"class":717},"\"conveyorData.Conveyor_Running\"",[697,933,721],{"class":707},[697,935,878],{"class":703},[697,937,727],{"class":707},[697,939,940,942,944,946,948,951,953,955],{"class":699,"line":770},[697,941,704],{"class":703},[697,943,708],{"class":707},[697,945,494],{"class":711},[697,947,714],{"class":707},[697,949,950],{"class":717},"\"conveyorData.Line4_State\"",[697,952,721],{"class":707},[697,954,724],{"class":703},[697,956,727],{"class":707},[697,958,959,961,963,965,967,970,972,974],{"class":699,"line":788},[697,960,704],{"class":703},[697,962,708],{"class":707},[697,964,494],{"class":711},[697,966,714],{"class":707},[697,968,969],{"class":717},"\"conveyorData.Line4_Fault\"",[697,971,721],{"class":707},[697,973,878],{"class":703},[697,975,727],{"class":707},[681,977,979],{"id":978},"create-custom-folder-structure","Create Custom Folder Structure",[18,981,982],{},"Starting from the section of code where the example folder structure is defined, delete it and replace it with our custom folder structure defined in our architecture -",[18,984,985,989],{},[48,986],{"alt":987,"src":988},"opc-folder-structure.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fopc-folder-structure.png","\nDelete the section of code starting from here -",[689,991,993],{"className":691,"code":992,"language":693,"meta":12,"style":12},"  const myDevice = namespace.addFolder(rootFolder.objects, {\n    \"browseName\": \"RaspberryPI-Zero-WLAN\"\n  });\n  const gpioFolder = namespace.addFolder(myDevice, { \"browseName\": \"GPIO\" });\n  const isoInputs = namespace.addFolder(gpioFolder, {\n    \"browseName\": \"Inputs\"\n  });\n  const isoOutputs = namespace.addFolder(gpioFolder, {\n    \"browseName\": \"Outputs\"\n  });\n",[191,994,995,1015,1026,1031,1058,1074,1083,1087,1102,1111],{"__ignoreMap":12},[697,996,997,1000,1003,1006,1009,1012],{"class":699,"line":700},[697,998,999],{"class":743},"  const",[697,1001,1002],{"class":703}," myDevice",[697,1004,1005],{"class":743}," =",[697,1007,1008],{"class":707}," namespace.",[697,1010,1011],{"class":711},"addFolder",[697,1013,1014],{"class":707},"(rootFolder.objects, {\n",[697,1016,1017,1020,1023],{"class":699,"line":730},[697,1018,1019],{"class":717},"    \"browseName\"",[697,1021,1022],{"class":707},": ",[697,1024,1025],{"class":717},"\"RaspberryPI-Zero-WLAN\"\n",[697,1027,1028],{"class":699,"line":750},[697,1029,1030],{"class":707},"  });\n",[697,1032,1033,1035,1038,1040,1042,1044,1047,1050,1052,1055],{"class":699,"line":761},[697,1034,999],{"class":743},[697,1036,1037],{"class":703}," gpioFolder",[697,1039,1005],{"class":743},[697,1041,1008],{"class":707},[697,1043,1011],{"class":711},[697,1045,1046],{"class":707},"(myDevice, { ",[697,1048,1049],{"class":717},"\"browseName\"",[697,1051,1022],{"class":707},[697,1053,1054],{"class":717},"\"GPIO\"",[697,1056,1057],{"class":707}," });\n",[697,1059,1060,1062,1065,1067,1069,1071],{"class":699,"line":770},[697,1061,999],{"class":743},[697,1063,1064],{"class":703}," isoInputs",[697,1066,1005],{"class":743},[697,1068,1008],{"class":707},[697,1070,1011],{"class":711},[697,1072,1073],{"class":707},"(gpioFolder, {\n",[697,1075,1076,1078,1080],{"class":699,"line":788},[697,1077,1019],{"class":717},[697,1079,1022],{"class":707},[697,1081,1082],{"class":717},"\"Inputs\"\n",[697,1084,1085],{"class":699,"line":794},[697,1086,1030],{"class":707},[697,1088,1089,1091,1094,1096,1098,1100],{"class":699,"line":805},[697,1090,999],{"class":743},[697,1092,1093],{"class":703}," isoOutputs",[697,1095,1005],{"class":743},[697,1097,1008],{"class":707},[697,1099,1011],{"class":711},[697,1101,1073],{"class":707},[697,1103,1104,1106,1108],{"class":699,"line":825},[697,1105,1019],{"class":717},[697,1107,1022],{"class":707},[697,1109,1110],{"class":717},"\"Outputs\"\n",[697,1112,1113],{"class":699,"line":831},[697,1114,1030],{"class":707},[18,1116,1117],{},"and replace it with the folder structure shown above -",[689,1119,1121],{"className":691,"code":1120,"language":693,"meta":12,"style":12},"  const myDevice = namespace.addFolder(rootFolder.objects, {\n    \"browseName\": \"Line 4 PLC\"\n  });\n  const conveyorFolder = namespace.addFolder(myDevice, { \n  \"browseName\": \"Conveyor\" \n  });\n  const conveyorBools = namespace.addFolder(conveyorFolder, {\n    \"browseName\": \"Bools\"\n  });\n  const conveyorDINTs = namespace.addFolder(conveyorFolder, {\n    \"browseName\": \"DINTs\"\n  });\n  const conveyorFloats = namespace.addFolder(conveyorFolder, {\n    \"browseName\": \"Floats\"\n  });\n",[191,1122,1123,1137,1146,1150,1166,1179,1183,1199,1208,1212,1227,1237,1242,1258,1268],{"__ignoreMap":12},[697,1124,1125,1127,1129,1131,1133,1135],{"class":699,"line":700},[697,1126,999],{"class":743},[697,1128,1002],{"class":703},[697,1130,1005],{"class":743},[697,1132,1008],{"class":707},[697,1134,1011],{"class":711},[697,1136,1014],{"class":707},[697,1138,1139,1141,1143],{"class":699,"line":730},[697,1140,1019],{"class":717},[697,1142,1022],{"class":707},[697,1144,1145],{"class":717},"\"Line 4 PLC\"\n",[697,1147,1148],{"class":699,"line":750},[697,1149,1030],{"class":707},[697,1151,1152,1154,1157,1159,1161,1163],{"class":699,"line":761},[697,1153,999],{"class":743},[697,1155,1156],{"class":703}," conveyorFolder",[697,1158,1005],{"class":743},[697,1160,1008],{"class":707},[697,1162,1011],{"class":711},[697,1164,1165],{"class":707},"(myDevice, { \n",[697,1167,1168,1171,1173,1176],{"class":699,"line":770},[697,1169,1170],{"class":717},"  \"browseName\"",[697,1172,1022],{"class":707},[697,1174,1175],{"class":717},"\"Conveyor\"",[697,1177,1178],{"class":707}," \n",[697,1180,1181],{"class":699,"line":788},[697,1182,1030],{"class":707},[697,1184,1185,1187,1190,1192,1194,1196],{"class":699,"line":794},[697,1186,999],{"class":743},[697,1188,1189],{"class":703}," conveyorBools",[697,1191,1005],{"class":743},[697,1193,1008],{"class":707},[697,1195,1011],{"class":711},[697,1197,1198],{"class":707},"(conveyorFolder, {\n",[697,1200,1201,1203,1205],{"class":699,"line":805},[697,1202,1019],{"class":717},[697,1204,1022],{"class":707},[697,1206,1207],{"class":717},"\"Bools\"\n",[697,1209,1210],{"class":699,"line":825},[697,1211,1030],{"class":707},[697,1213,1214,1216,1219,1221,1223,1225],{"class":699,"line":831},[697,1215,999],{"class":743},[697,1217,1218],{"class":703}," conveyorDINTs",[697,1220,1005],{"class":743},[697,1222,1008],{"class":707},[697,1224,1011],{"class":711},[697,1226,1198],{"class":707},[697,1228,1230,1232,1234],{"class":699,"line":1229},11,[697,1231,1019],{"class":717},[697,1233,1022],{"class":707},[697,1235,1236],{"class":717},"\"DINTs\"\n",[697,1238,1240],{"class":699,"line":1239},12,[697,1241,1030],{"class":707},[697,1243,1245,1247,1250,1252,1254,1256],{"class":699,"line":1244},13,[697,1246,999],{"class":743},[697,1248,1249],{"class":703}," conveyorFloats",[697,1251,1005],{"class":743},[697,1253,1008],{"class":707},[697,1255,1011],{"class":711},[697,1257,1198],{"class":707},[697,1259,1261,1263,1265],{"class":699,"line":1260},14,[697,1262,1019],{"class":717},[697,1264,1022],{"class":707},[697,1266,1267],{"class":717},"\"Floats\"\n",[697,1269,1271],{"class":699,"line":1270},15,[697,1272,1030],{"class":707},[681,1274,1276],{"id":1275},"define-opc-ua-nodes","Define OPC UA Nodes",[18,1278,1279],{},"Now we can construct the nodes for each context variable.",[18,1281,1282],{},[48,1283],{"alt":1284,"src":1285},"opc-nodes.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fopc-nodes.png",[18,1287,1288,1289,1292,1293,1296],{},"Delete the section of code defining the nodes for ",[191,1290,1291],{},"isoInput1"," through ",[191,1294,1295],{},"isoOutput8","  -",[689,1298,1300],{"className":691,"code":1299,"language":693,"meta":12,"style":12},"  const gpioDI1 = namespace.addVariable({\n    \"organizedBy\": isoInputs,\n    \"browseName\": \"I1\",\n    \"nodeId\": \"ns=1;s=Isolated_Input1\",\n    \"dataType\": \"Double\",\n    \"value\": {\n    ...\n      \"set\": function(variant) {\n        flexServerInternals.sandboxFlowContext.set(\n          \"isoOutput8\",\n          parseFloat(variant.value)\n        );\n        return opcua.StatusCodes.Good;\n      }\n    }\n  });   \n",[191,1301,1302,1319,1327,1338,1350,1362,1370,1375,1394,1403,1410,1418,1423,1431,1436,1441],{"__ignoreMap":12},[697,1303,1304,1306,1309,1311,1313,1316],{"class":699,"line":700},[697,1305,999],{"class":743},[697,1307,1308],{"class":703}," gpioDI1",[697,1310,1005],{"class":743},[697,1312,1008],{"class":707},[697,1314,1315],{"class":711},"addVariable",[697,1317,1318],{"class":707},"({\n",[697,1320,1321,1324],{"class":699,"line":730},[697,1322,1323],{"class":717},"    \"organizedBy\"",[697,1325,1326],{"class":707},": isoInputs,\n",[697,1328,1329,1331,1333,1336],{"class":699,"line":750},[697,1330,1019],{"class":717},[697,1332,1022],{"class":707},[697,1334,1335],{"class":717},"\"I1\"",[697,1337,767],{"class":707},[697,1339,1340,1343,1345,1348],{"class":699,"line":761},[697,1341,1342],{"class":717},"    \"nodeId\"",[697,1344,1022],{"class":707},[697,1346,1347],{"class":717},"\"ns=1;s=Isolated_Input1\"",[697,1349,767],{"class":707},[697,1351,1352,1355,1357,1360],{"class":699,"line":770},[697,1353,1354],{"class":717},"    \"dataType\"",[697,1356,1022],{"class":707},[697,1358,1359],{"class":717},"\"Double\"",[697,1361,767],{"class":707},[697,1363,1364,1367],{"class":699,"line":788},[697,1365,1366],{"class":717},"    \"value\"",[697,1368,1369],{"class":707},": {\n",[697,1371,1372],{"class":699,"line":794},[697,1373,1374],{"class":743},"    ...\n",[697,1376,1377,1380,1382,1385,1387,1391],{"class":699,"line":805},[697,1378,1379],{"class":717},"      \"set\"",[697,1381,1022],{"class":707},[697,1383,1384],{"class":743},"function",[697,1386,714],{"class":707},[697,1388,1390],{"class":1389},"s4XuR","variant",[697,1392,1393],{"class":707},") {\n",[697,1395,1396,1399,1401],{"class":699,"line":825},[697,1397,1398],{"class":707},"        flexServerInternals.sandboxFlowContext.",[697,1400,494],{"class":711},[697,1402,758],{"class":707},[697,1404,1405,1408],{"class":699,"line":831},[697,1406,1407],{"class":717},"          \"isoOutput8\"",[697,1409,767],{"class":707},[697,1411,1412,1415],{"class":699,"line":1229},[697,1413,1414],{"class":711},"          parseFloat",[697,1416,1417],{"class":707},"(variant.value)\n",[697,1419,1420],{"class":699,"line":1239},[697,1421,1422],{"class":707},"        );\n",[697,1424,1425,1428],{"class":699,"line":1244},[697,1426,1427],{"class":743},"        return",[697,1429,1430],{"class":707}," opcua.StatusCodes.Good;\n",[697,1432,1433],{"class":699,"line":1260},[697,1434,1435],{"class":707},"      }\n",[697,1437,1438],{"class":699,"line":1270},[697,1439,1440],{"class":707},"    }\n",[697,1442,1444],{"class":699,"line":1443},16,[697,1445,1030],{"class":707},[18,1447,1448],{},"And replace it with our custom nodes, paying respect to the folder structure we defined in our architecture -",[689,1450,1452],{"className":691,"code":1451,"language":693,"meta":12,"style":12},"  \u002F\u002F Construct Nodes\n  const Conveyor_RTS = namespace.addVariable({\n    \"organizedBy\": conveyorBools,\n    \"browseName\": \"Conveyor Ready to Start\",\n    \"nodeId\": \"ns=1;s=Conveyor_RTS\",\n    \"dataType\": \"Boolean\",\n    \"value\": {\n      \"get\": function () {\n        return new Variant({\n          \"dataType\": DataType.Boolean,\n          \"value\": flexServerInternals.sandboxFlowContext.get(\"conveyorData.Conveyor_RTS\")\n        });\n      },\n      \"set\": function (variant) {\n        flexServerInternals.sandboxFlowContext.set(\n          \"conveyorData.Conveyor_RTS\",\n          variant.value\n        );\n        return opcua.StatusCodes.Good;\n      }\n    }\n  });\n\n  const Robot_RTS = namespace.addVariable({\n    \"organizedBy\": conveyorBools,\n    \"browseName\": \"Robot Ready to Start\",\n    \"nodeId\": \"ns=1;s=Robot_RTS\",\n    \"dataType\": \"Boolean\",\n    \"value\": {\n      \"get\": function () {\n        return new Variant({\n          \"dataType\": DataType.Boolean,\n          \"value\": flexServerInternals.sandboxFlowContext.get(\"conveyorData.Robot_RTS\")\n        });\n      },\n      \"set\": function (variant) {\n        flexServerInternals.sandboxFlowContext.set(\n          \"conveyorData.Robot_RTS\",\n          variant.value\n        );\n        return opcua.StatusCodes.Good;\n      }\n    }\n  });\n\n  const Conveyor_Running = namespace.addVariable({\n    \"organizedBy\": conveyorBools,\n    \"browseName\": \"Conveyor Running\",\n    \"nodeId\": \"ns=1;s=Conveyor_Running\",\n    \"dataType\": \"Boolean\",\n    \"value\": {\n      \"get\": function () {\n        return new Variant({\n          \"dataType\": DataType.Boolean,\n          \"value\": flexServerInternals.sandboxFlowContext.get(\"conveyorData.Conveyor_Running\")\n        });\n      },\n      \"set\": function (variant) {\n        flexServerInternals.sandboxFlowContext.set(\n          \"conveyorData.Conveyor_Running\",\n          variant.value\n        );\n        return opcua.StatusCodes.Good;\n      }\n    }\n  });\n\n  const Line4_Fault = namespace.addVariable({\n    \"organizedBy\": conveyorBools,\n    \"browseName\": \"Line 4 Faulted\",\n    \"nodeId\": \"ns=1;s=Line4_Fault\",\n    \"dataType\": \"Boolean\",\n    \"value\": {\n      \"get\": function () {\n        return new Variant({\n          \"dataType\": DataType.Boolean,\n          \"value\": flexServerInternals.sandboxFlowContext.get(\"conveyorData.Line4_Fault\")\n        });\n      },\n      \"set\": function (variant) {\n        flexServerInternals.sandboxFlowContext.set(\n          \"conveyorData.Line4_Fault\",\n          variant.value\n        );\n        return opcua.StatusCodes.Good;\n      }\n    }\n  });\n\n  const Line4_State = namespace.addVariable({\n    \"organizedBy\": conveyorDINTs,\n    \"browseName\": \"Line 4 State\",\n    \"nodeId\": \"ns=1;s=Line4_State\",\n    \"dataType\": \"Int32\",\n    \"value\": {\n      \"get\": function () {\n        return new Variant({\n          \"dataType\": DataType.Int32,\n          \"value\": flexServerInternals.sandboxFlowContext.get(\"conveyorData.Line4_State\")\n        });\n      },\n      \"set\": function (variant) {\n        flexServerInternals.sandboxFlowContext.set(\n          \"conveyorData.Line4_State\",\n          variant.value\n        );\n        return opcua.StatusCodes.Good;\n      }\n    }\n  });\n\n  const Robot_Position = namespace.addVariable({\n    \"organizedBy\": conveyorFloats,\n    \"browseName\": \"Robot Axis A1 Position\",\n    \"nodeId\": \"ns=1;s=Robot_Position\",\n    \"dataType\": \"Float\",\n    \"value\": {\n      \"get\": function () {\n        return new Variant({\n          \"dataType\": DataType.Float,\n          \"value\": flexServerInternals.sandboxFlowContext.get(\"conveyorData.Robot_Position\")\n        });\n      },\n      \"set\": function (variant) {\n        flexServerInternals.sandboxFlowContext.set(\n          \"conveyorData.Robot_Position\",\n          parseFloat(variant.value)\n        );\n        return opcua.StatusCodes.Good;\n      }\n    }\n  });\n",[191,1453,1454,1460,1475,1482,1493,1504,1515,1521,1533,1545,1553,1571,1576,1581,1596,1604,1611,1617,1622,1629,1634,1639,1644,1650,1666,1673,1685,1697,1708,1715,1726,1737,1744,1759,1764,1769,1784,1793,1801,1806,1811,1818,1823,1828,1833,1838,1854,1861,1873,1885,1896,1903,1914,1925,1932,1947,1952,1957,1972,1981,1989,1994,1999,2006,2011,2016,2021,2026,2042,2049,2061,2073,2084,2091,2102,2113,2120,2135,2140,2145,2160,2169,2177,2182,2187,2194,2199,2204,2209,2214,2230,2238,2250,2262,2274,2281,2292,2303,2311,2326,2331,2336,2351,2360,2368,2373,2378,2385,2390,2395,2400,2405,2421,2429,2441,2453,2465,2472,2483,2494,2502,2517,2522,2527,2542,2551,2559,2566,2571,2578,2583,2588],{"__ignoreMap":12},[697,1455,1456],{"class":699,"line":700},[697,1457,1459],{"class":1458},"sJ8bj","  \u002F\u002F Construct Nodes\n",[697,1461,1462,1464,1467,1469,1471,1473],{"class":699,"line":730},[697,1463,999],{"class":743},[697,1465,1466],{"class":703}," Conveyor_RTS",[697,1468,1005],{"class":743},[697,1470,1008],{"class":707},[697,1472,1315],{"class":711},[697,1474,1318],{"class":707},[697,1476,1477,1479],{"class":699,"line":750},[697,1478,1323],{"class":717},[697,1480,1481],{"class":707},": conveyorBools,\n",[697,1483,1484,1486,1488,1491],{"class":699,"line":761},[697,1485,1019],{"class":717},[697,1487,1022],{"class":707},[697,1489,1490],{"class":717},"\"Conveyor Ready to Start\"",[697,1492,767],{"class":707},[697,1494,1495,1497,1499,1502],{"class":699,"line":770},[697,1496,1342],{"class":717},[697,1498,1022],{"class":707},[697,1500,1501],{"class":717},"\"ns=1;s=Conveyor_RTS\"",[697,1503,767],{"class":707},[697,1505,1506,1508,1510,1513],{"class":699,"line":788},[697,1507,1354],{"class":717},[697,1509,1022],{"class":707},[697,1511,1512],{"class":717},"\"Boolean\"",[697,1514,767],{"class":707},[697,1516,1517,1519],{"class":699,"line":794},[697,1518,1366],{"class":717},[697,1520,1369],{"class":707},[697,1522,1523,1526,1528,1530],{"class":699,"line":805},[697,1524,1525],{"class":717},"      \"get\"",[697,1527,1022],{"class":707},[697,1529,1384],{"class":743},[697,1531,1532],{"class":707}," () {\n",[697,1534,1535,1537,1540,1543],{"class":699,"line":825},[697,1536,1427],{"class":743},[697,1538,1539],{"class":743}," new",[697,1541,1542],{"class":711}," Variant",[697,1544,1318],{"class":707},[697,1546,1547,1550],{"class":699,"line":831},[697,1548,1549],{"class":717},"          \"dataType\"",[697,1551,1552],{"class":707},": DataType.Boolean,\n",[697,1554,1555,1558,1561,1564,1566,1568],{"class":699,"line":1229},[697,1556,1557],{"class":717},"          \"value\"",[697,1559,1560],{"class":707},": flexServerInternals.sandboxFlowContext.",[697,1562,1563],{"class":711},"get",[697,1565,714],{"class":707},[697,1567,873],{"class":717},[697,1569,1570],{"class":707},")\n",[697,1572,1573],{"class":699,"line":1239},[697,1574,1575],{"class":707},"        });\n",[697,1577,1578],{"class":699,"line":1244},[697,1579,1580],{"class":707},"      },\n",[697,1582,1583,1585,1587,1589,1592,1594],{"class":699,"line":1260},[697,1584,1379],{"class":717},[697,1586,1022],{"class":707},[697,1588,1384],{"class":743},[697,1590,1591],{"class":707}," (",[697,1593,1390],{"class":1389},[697,1595,1393],{"class":707},[697,1597,1598,1600,1602],{"class":699,"line":1270},[697,1599,1398],{"class":707},[697,1601,494],{"class":711},[697,1603,758],{"class":707},[697,1605,1606,1609],{"class":699,"line":1443},[697,1607,1608],{"class":717},"          \"conveyorData.Conveyor_RTS\"",[697,1610,767],{"class":707},[697,1612,1614],{"class":699,"line":1613},17,[697,1615,1616],{"class":707},"          variant.value\n",[697,1618,1620],{"class":699,"line":1619},18,[697,1621,1422],{"class":707},[697,1623,1625,1627],{"class":699,"line":1624},19,[697,1626,1427],{"class":743},[697,1628,1430],{"class":707},[697,1630,1632],{"class":699,"line":1631},20,[697,1633,1435],{"class":707},[697,1635,1637],{"class":699,"line":1636},21,[697,1638,1440],{"class":707},[697,1640,1642],{"class":699,"line":1641},22,[697,1643,1030],{"class":707},[697,1645,1647],{"class":699,"line":1646},23,[697,1648,1649],{"emptyLinePlaceholder":426},"\n",[697,1651,1653,1655,1658,1660,1662,1664],{"class":699,"line":1652},24,[697,1654,999],{"class":743},[697,1656,1657],{"class":703}," Robot_RTS",[697,1659,1005],{"class":743},[697,1661,1008],{"class":707},[697,1663,1315],{"class":711},[697,1665,1318],{"class":707},[697,1667,1669,1671],{"class":699,"line":1668},25,[697,1670,1323],{"class":717},[697,1672,1481],{"class":707},[697,1674,1676,1678,1680,1683],{"class":699,"line":1675},26,[697,1677,1019],{"class":717},[697,1679,1022],{"class":707},[697,1681,1682],{"class":717},"\"Robot Ready to Start\"",[697,1684,767],{"class":707},[697,1686,1688,1690,1692,1695],{"class":699,"line":1687},27,[697,1689,1342],{"class":717},[697,1691,1022],{"class":707},[697,1693,1694],{"class":717},"\"ns=1;s=Robot_RTS\"",[697,1696,767],{"class":707},[697,1698,1700,1702,1704,1706],{"class":699,"line":1699},28,[697,1701,1354],{"class":717},[697,1703,1022],{"class":707},[697,1705,1512],{"class":717},[697,1707,767],{"class":707},[697,1709,1711,1713],{"class":699,"line":1710},29,[697,1712,1366],{"class":717},[697,1714,1369],{"class":707},[697,1716,1718,1720,1722,1724],{"class":699,"line":1717},30,[697,1719,1525],{"class":717},[697,1721,1022],{"class":707},[697,1723,1384],{"class":743},[697,1725,1532],{"class":707},[697,1727,1729,1731,1733,1735],{"class":699,"line":1728},31,[697,1730,1427],{"class":743},[697,1732,1539],{"class":743},[697,1734,1542],{"class":711},[697,1736,1318],{"class":707},[697,1738,1740,1742],{"class":699,"line":1739},32,[697,1741,1549],{"class":717},[697,1743,1552],{"class":707},[697,1745,1747,1749,1751,1753,1755,1757],{"class":699,"line":1746},33,[697,1748,1557],{"class":717},[697,1750,1560],{"class":707},[697,1752,1563],{"class":711},[697,1754,714],{"class":707},[697,1756,893],{"class":717},[697,1758,1570],{"class":707},[697,1760,1762],{"class":699,"line":1761},34,[697,1763,1575],{"class":707},[697,1765,1767],{"class":699,"line":1766},35,[697,1768,1580],{"class":707},[697,1770,1772,1774,1776,1778,1780,1782],{"class":699,"line":1771},36,[697,1773,1379],{"class":717},[697,1775,1022],{"class":707},[697,1777,1384],{"class":743},[697,1779,1591],{"class":707},[697,1781,1390],{"class":1389},[697,1783,1393],{"class":707},[697,1785,1787,1789,1791],{"class":699,"line":1786},37,[697,1788,1398],{"class":707},[697,1790,494],{"class":711},[697,1792,758],{"class":707},[697,1794,1796,1799],{"class":699,"line":1795},38,[697,1797,1798],{"class":717},"          \"conveyorData.Robot_RTS\"",[697,1800,767],{"class":707},[697,1802,1804],{"class":699,"line":1803},39,[697,1805,1616],{"class":707},[697,1807,1809],{"class":699,"line":1808},40,[697,1810,1422],{"class":707},[697,1812,1814,1816],{"class":699,"line":1813},41,[697,1815,1427],{"class":743},[697,1817,1430],{"class":707},[697,1819,1821],{"class":699,"line":1820},42,[697,1822,1435],{"class":707},[697,1824,1826],{"class":699,"line":1825},43,[697,1827,1440],{"class":707},[697,1829,1831],{"class":699,"line":1830},44,[697,1832,1030],{"class":707},[697,1834,1836],{"class":699,"line":1835},45,[697,1837,1649],{"emptyLinePlaceholder":426},[697,1839,1841,1843,1846,1848,1850,1852],{"class":699,"line":1840},46,[697,1842,999],{"class":743},[697,1844,1845],{"class":703}," Conveyor_Running",[697,1847,1005],{"class":743},[697,1849,1008],{"class":707},[697,1851,1315],{"class":711},[697,1853,1318],{"class":707},[697,1855,1857,1859],{"class":699,"line":1856},47,[697,1858,1323],{"class":717},[697,1860,1481],{"class":707},[697,1862,1864,1866,1868,1871],{"class":699,"line":1863},48,[697,1865,1019],{"class":717},[697,1867,1022],{"class":707},[697,1869,1870],{"class":717},"\"Conveyor Running\"",[697,1872,767],{"class":707},[697,1874,1876,1878,1880,1883],{"class":699,"line":1875},49,[697,1877,1342],{"class":717},[697,1879,1022],{"class":707},[697,1881,1882],{"class":717},"\"ns=1;s=Conveyor_Running\"",[697,1884,767],{"class":707},[697,1886,1888,1890,1892,1894],{"class":699,"line":1887},50,[697,1889,1354],{"class":717},[697,1891,1022],{"class":707},[697,1893,1512],{"class":717},[697,1895,767],{"class":707},[697,1897,1899,1901],{"class":699,"line":1898},51,[697,1900,1366],{"class":717},[697,1902,1369],{"class":707},[697,1904,1906,1908,1910,1912],{"class":699,"line":1905},52,[697,1907,1525],{"class":717},[697,1909,1022],{"class":707},[697,1911,1384],{"class":743},[697,1913,1532],{"class":707},[697,1915,1917,1919,1921,1923],{"class":699,"line":1916},53,[697,1918,1427],{"class":743},[697,1920,1539],{"class":743},[697,1922,1542],{"class":711},[697,1924,1318],{"class":707},[697,1926,1928,1930],{"class":699,"line":1927},54,[697,1929,1549],{"class":717},[697,1931,1552],{"class":707},[697,1933,1935,1937,1939,1941,1943,1945],{"class":699,"line":1934},55,[697,1936,1557],{"class":717},[697,1938,1560],{"class":707},[697,1940,1563],{"class":711},[697,1942,714],{"class":707},[697,1944,931],{"class":717},[697,1946,1570],{"class":707},[697,1948,1950],{"class":699,"line":1949},56,[697,1951,1575],{"class":707},[697,1953,1955],{"class":699,"line":1954},57,[697,1956,1580],{"class":707},[697,1958,1960,1962,1964,1966,1968,1970],{"class":699,"line":1959},58,[697,1961,1379],{"class":717},[697,1963,1022],{"class":707},[697,1965,1384],{"class":743},[697,1967,1591],{"class":707},[697,1969,1390],{"class":1389},[697,1971,1393],{"class":707},[697,1973,1975,1977,1979],{"class":699,"line":1974},59,[697,1976,1398],{"class":707},[697,1978,494],{"class":711},[697,1980,758],{"class":707},[697,1982,1984,1987],{"class":699,"line":1983},60,[697,1985,1986],{"class":717},"          \"conveyorData.Conveyor_Running\"",[697,1988,767],{"class":707},[697,1990,1992],{"class":699,"line":1991},61,[697,1993,1616],{"class":707},[697,1995,1997],{"class":699,"line":1996},62,[697,1998,1422],{"class":707},[697,2000,2002,2004],{"class":699,"line":2001},63,[697,2003,1427],{"class":743},[697,2005,1430],{"class":707},[697,2007,2009],{"class":699,"line":2008},64,[697,2010,1435],{"class":707},[697,2012,2014],{"class":699,"line":2013},65,[697,2015,1440],{"class":707},[697,2017,2019],{"class":699,"line":2018},66,[697,2020,1030],{"class":707},[697,2022,2024],{"class":699,"line":2023},67,[697,2025,1649],{"emptyLinePlaceholder":426},[697,2027,2029,2031,2034,2036,2038,2040],{"class":699,"line":2028},68,[697,2030,999],{"class":743},[697,2032,2033],{"class":703}," Line4_Fault",[697,2035,1005],{"class":743},[697,2037,1008],{"class":707},[697,2039,1315],{"class":711},[697,2041,1318],{"class":707},[697,2043,2045,2047],{"class":699,"line":2044},69,[697,2046,1323],{"class":717},[697,2048,1481],{"class":707},[697,2050,2052,2054,2056,2059],{"class":699,"line":2051},70,[697,2053,1019],{"class":717},[697,2055,1022],{"class":707},[697,2057,2058],{"class":717},"\"Line 4 Faulted\"",[697,2060,767],{"class":707},[697,2062,2064,2066,2068,2071],{"class":699,"line":2063},71,[697,2065,1342],{"class":717},[697,2067,1022],{"class":707},[697,2069,2070],{"class":717},"\"ns=1;s=Line4_Fault\"",[697,2072,767],{"class":707},[697,2074,2076,2078,2080,2082],{"class":699,"line":2075},72,[697,2077,1354],{"class":717},[697,2079,1022],{"class":707},[697,2081,1512],{"class":717},[697,2083,767],{"class":707},[697,2085,2087,2089],{"class":699,"line":2086},73,[697,2088,1366],{"class":717},[697,2090,1369],{"class":707},[697,2092,2094,2096,2098,2100],{"class":699,"line":2093},74,[697,2095,1525],{"class":717},[697,2097,1022],{"class":707},[697,2099,1384],{"class":743},[697,2101,1532],{"class":707},[697,2103,2105,2107,2109,2111],{"class":699,"line":2104},75,[697,2106,1427],{"class":743},[697,2108,1539],{"class":743},[697,2110,1542],{"class":711},[697,2112,1318],{"class":707},[697,2114,2116,2118],{"class":699,"line":2115},76,[697,2117,1549],{"class":717},[697,2119,1552],{"class":707},[697,2121,2123,2125,2127,2129,2131,2133],{"class":699,"line":2122},77,[697,2124,1557],{"class":717},[697,2126,1560],{"class":707},[697,2128,1563],{"class":711},[697,2130,714],{"class":707},[697,2132,969],{"class":717},[697,2134,1570],{"class":707},[697,2136,2138],{"class":699,"line":2137},78,[697,2139,1575],{"class":707},[697,2141,2143],{"class":699,"line":2142},79,[697,2144,1580],{"class":707},[697,2146,2148,2150,2152,2154,2156,2158],{"class":699,"line":2147},80,[697,2149,1379],{"class":717},[697,2151,1022],{"class":707},[697,2153,1384],{"class":743},[697,2155,1591],{"class":707},[697,2157,1390],{"class":1389},[697,2159,1393],{"class":707},[697,2161,2163,2165,2167],{"class":699,"line":2162},81,[697,2164,1398],{"class":707},[697,2166,494],{"class":711},[697,2168,758],{"class":707},[697,2170,2172,2175],{"class":699,"line":2171},82,[697,2173,2174],{"class":717},"          \"conveyorData.Line4_Fault\"",[697,2176,767],{"class":707},[697,2178,2180],{"class":699,"line":2179},83,[697,2181,1616],{"class":707},[697,2183,2185],{"class":699,"line":2184},84,[697,2186,1422],{"class":707},[697,2188,2190,2192],{"class":699,"line":2189},85,[697,2191,1427],{"class":743},[697,2193,1430],{"class":707},[697,2195,2197],{"class":699,"line":2196},86,[697,2198,1435],{"class":707},[697,2200,2202],{"class":699,"line":2201},87,[697,2203,1440],{"class":707},[697,2205,2207],{"class":699,"line":2206},88,[697,2208,1030],{"class":707},[697,2210,2212],{"class":699,"line":2211},89,[697,2213,1649],{"emptyLinePlaceholder":426},[697,2215,2217,2219,2222,2224,2226,2228],{"class":699,"line":2216},90,[697,2218,999],{"class":743},[697,2220,2221],{"class":703}," Line4_State",[697,2223,1005],{"class":743},[697,2225,1008],{"class":707},[697,2227,1315],{"class":711},[697,2229,1318],{"class":707},[697,2231,2233,2235],{"class":699,"line":2232},91,[697,2234,1323],{"class":717},[697,2236,2237],{"class":707},": conveyorDINTs,\n",[697,2239,2241,2243,2245,2248],{"class":699,"line":2240},92,[697,2242,1019],{"class":717},[697,2244,1022],{"class":707},[697,2246,2247],{"class":717},"\"Line 4 State\"",[697,2249,767],{"class":707},[697,2251,2253,2255,2257,2260],{"class":699,"line":2252},93,[697,2254,1342],{"class":717},[697,2256,1022],{"class":707},[697,2258,2259],{"class":717},"\"ns=1;s=Line4_State\"",[697,2261,767],{"class":707},[697,2263,2265,2267,2269,2272],{"class":699,"line":2264},94,[697,2266,1354],{"class":717},[697,2268,1022],{"class":707},[697,2270,2271],{"class":717},"\"Int32\"",[697,2273,767],{"class":707},[697,2275,2277,2279],{"class":699,"line":2276},95,[697,2278,1366],{"class":717},[697,2280,1369],{"class":707},[697,2282,2284,2286,2288,2290],{"class":699,"line":2283},96,[697,2285,1525],{"class":717},[697,2287,1022],{"class":707},[697,2289,1384],{"class":743},[697,2291,1532],{"class":707},[697,2293,2295,2297,2299,2301],{"class":699,"line":2294},97,[697,2296,1427],{"class":743},[697,2298,1539],{"class":743},[697,2300,1542],{"class":711},[697,2302,1318],{"class":707},[697,2304,2306,2308],{"class":699,"line":2305},98,[697,2307,1549],{"class":717},[697,2309,2310],{"class":707},": DataType.Int32,\n",[697,2312,2314,2316,2318,2320,2322,2324],{"class":699,"line":2313},99,[697,2315,1557],{"class":717},[697,2317,1560],{"class":707},[697,2319,1563],{"class":711},[697,2321,714],{"class":707},[697,2323,950],{"class":717},[697,2325,1570],{"class":707},[697,2327,2329],{"class":699,"line":2328},100,[697,2330,1575],{"class":707},[697,2332,2334],{"class":699,"line":2333},101,[697,2335,1580],{"class":707},[697,2337,2339,2341,2343,2345,2347,2349],{"class":699,"line":2338},102,[697,2340,1379],{"class":717},[697,2342,1022],{"class":707},[697,2344,1384],{"class":743},[697,2346,1591],{"class":707},[697,2348,1390],{"class":1389},[697,2350,1393],{"class":707},[697,2352,2354,2356,2358],{"class":699,"line":2353},103,[697,2355,1398],{"class":707},[697,2357,494],{"class":711},[697,2359,758],{"class":707},[697,2361,2363,2366],{"class":699,"line":2362},104,[697,2364,2365],{"class":717},"          \"conveyorData.Line4_State\"",[697,2367,767],{"class":707},[697,2369,2371],{"class":699,"line":2370},105,[697,2372,1616],{"class":707},[697,2374,2376],{"class":699,"line":2375},106,[697,2377,1422],{"class":707},[697,2379,2381,2383],{"class":699,"line":2380},107,[697,2382,1427],{"class":743},[697,2384,1430],{"class":707},[697,2386,2388],{"class":699,"line":2387},108,[697,2389,1435],{"class":707},[697,2391,2393],{"class":699,"line":2392},109,[697,2394,1440],{"class":707},[697,2396,2398],{"class":699,"line":2397},110,[697,2399,1030],{"class":707},[697,2401,2403],{"class":699,"line":2402},111,[697,2404,1649],{"emptyLinePlaceholder":426},[697,2406,2408,2410,2413,2415,2417,2419],{"class":699,"line":2407},112,[697,2409,999],{"class":743},[697,2411,2412],{"class":703}," Robot_Position",[697,2414,1005],{"class":743},[697,2416,1008],{"class":707},[697,2418,1315],{"class":711},[697,2420,1318],{"class":707},[697,2422,2424,2426],{"class":699,"line":2423},113,[697,2425,1323],{"class":717},[697,2427,2428],{"class":707},": conveyorFloats,\n",[697,2430,2432,2434,2436,2439],{"class":699,"line":2431},114,[697,2433,1019],{"class":717},[697,2435,1022],{"class":707},[697,2437,2438],{"class":717},"\"Robot Axis A1 Position\"",[697,2440,767],{"class":707},[697,2442,2444,2446,2448,2451],{"class":699,"line":2443},115,[697,2445,1342],{"class":717},[697,2447,1022],{"class":707},[697,2449,2450],{"class":717},"\"ns=1;s=Robot_Position\"",[697,2452,767],{"class":707},[697,2454,2456,2458,2460,2463],{"class":699,"line":2455},116,[697,2457,1354],{"class":717},[697,2459,1022],{"class":707},[697,2461,2462],{"class":717},"\"Float\"",[697,2464,767],{"class":707},[697,2466,2468,2470],{"class":699,"line":2467},117,[697,2469,1366],{"class":717},[697,2471,1369],{"class":707},[697,2473,2475,2477,2479,2481],{"class":699,"line":2474},118,[697,2476,1525],{"class":717},[697,2478,1022],{"class":707},[697,2480,1384],{"class":743},[697,2482,1532],{"class":707},[697,2484,2486,2488,2490,2492],{"class":699,"line":2485},119,[697,2487,1427],{"class":743},[697,2489,1539],{"class":743},[697,2491,1542],{"class":711},[697,2493,1318],{"class":707},[697,2495,2497,2499],{"class":699,"line":2496},120,[697,2498,1549],{"class":717},[697,2500,2501],{"class":707},": DataType.Float,\n",[697,2503,2505,2507,2509,2511,2513,2515],{"class":699,"line":2504},121,[697,2506,1557],{"class":717},[697,2508,1560],{"class":707},[697,2510,1563],{"class":711},[697,2512,714],{"class":707},[697,2514,912],{"class":717},[697,2516,1570],{"class":707},[697,2518,2520],{"class":699,"line":2519},122,[697,2521,1575],{"class":707},[697,2523,2525],{"class":699,"line":2524},123,[697,2526,1580],{"class":707},[697,2528,2530,2532,2534,2536,2538,2540],{"class":699,"line":2529},124,[697,2531,1379],{"class":717},[697,2533,1022],{"class":707},[697,2535,1384],{"class":743},[697,2537,1591],{"class":707},[697,2539,1390],{"class":1389},[697,2541,1393],{"class":707},[697,2543,2545,2547,2549],{"class":699,"line":2544},125,[697,2546,1398],{"class":707},[697,2548,494],{"class":711},[697,2550,758],{"class":707},[697,2552,2554,2557],{"class":699,"line":2553},126,[697,2555,2556],{"class":717},"          \"conveyorData.Robot_Position\"",[697,2558,767],{"class":707},[697,2560,2562,2564],{"class":699,"line":2561},127,[697,2563,1414],{"class":711},[697,2565,1417],{"class":707},[697,2567,2569],{"class":699,"line":2568},128,[697,2570,1422],{"class":707},[697,2572,2574,2576],{"class":699,"line":2573},129,[697,2575,1427],{"class":743},[697,2577,1430],{"class":707},[697,2579,2581],{"class":699,"line":2580},130,[697,2582,1435],{"class":707},[697,2584,2586],{"class":699,"line":2585},131,[697,2587,1440],{"class":707},[697,2589,2591],{"class":699,"line":2590},132,[697,2592,1030],{"class":707},[681,2594,2596],{"id":2595},"define-browser-views","Define Browser Views",[18,2598,2599],{},"Last, let’s create some custom views. Delete the code starting from -",[689,2601,2603],{"className":691,"code":2602,"language":693,"meta":12,"style":12},"  \u002F\u002F------------------------------------------------------------------------------\n  \u002F\u002F Add a view\n  \u002F\u002F------------------------------------------------------------------------------\n  const viewDI = namespace.addView({\n    \"organizedBy\": rootFolder.views,\n    \"browseName\": \"RPIW0-Digital-Ins\"\n  });\n  ...\n    viewDO.addReference({\n    \"referenceType\": \"Organizes\",\n    \"nodeId\": gpioDO8.nodeId\n  });\n",[191,2604,2605,2610,2615,2619,2635,2642,2651,2655,2660,2670,2682,2689],{"__ignoreMap":12},[697,2606,2607],{"class":699,"line":700},[697,2608,2609],{"class":1458},"  \u002F\u002F------------------------------------------------------------------------------\n",[697,2611,2612],{"class":699,"line":730},[697,2613,2614],{"class":1458},"  \u002F\u002F Add a view\n",[697,2616,2617],{"class":699,"line":750},[697,2618,2609],{"class":1458},[697,2620,2621,2623,2626,2628,2630,2633],{"class":699,"line":761},[697,2622,999],{"class":743},[697,2624,2625],{"class":703}," viewDI",[697,2627,1005],{"class":743},[697,2629,1008],{"class":707},[697,2631,2632],{"class":711},"addView",[697,2634,1318],{"class":707},[697,2636,2637,2639],{"class":699,"line":770},[697,2638,1323],{"class":717},[697,2640,2641],{"class":707},": rootFolder.views,\n",[697,2643,2644,2646,2648],{"class":699,"line":788},[697,2645,1019],{"class":717},[697,2647,1022],{"class":707},[697,2649,2650],{"class":717},"\"RPIW0-Digital-Ins\"\n",[697,2652,2653],{"class":699,"line":794},[697,2654,1030],{"class":707},[697,2656,2657],{"class":699,"line":805},[697,2658,2659],{"class":743},"  ...\n",[697,2661,2662,2665,2668],{"class":699,"line":825},[697,2663,2664],{"class":707},"    viewDO.",[697,2666,2667],{"class":711},"addReference",[697,2669,1318],{"class":707},[697,2671,2672,2675,2677,2680],{"class":699,"line":831},[697,2673,2674],{"class":717},"    \"referenceType\"",[697,2676,1022],{"class":707},[697,2678,2679],{"class":717},"\"Organizes\"",[697,2681,767],{"class":707},[697,2683,2684,2686],{"class":699,"line":1229},[697,2685,1342],{"class":717},[697,2687,2688],{"class":707},": gpioDO8.nodeId\n",[697,2690,2691],{"class":699,"line":1239},[697,2692,1030],{"class":707},[18,2694,2695],{},"And replace with a custom view of your choice.  I’ve defined a view that splits the bools, DINTs, and Reals.",[689,2697,2699],{"className":691,"code":2698,"language":693,"meta":12,"style":12},"  const viewBools = namespace.addView({\n    \"organizedBy\": rootFolder.views,\n    \"browseName\": \"Line 4 Conveyor Bools\"\n  });\n\n  const viewDINTs = namespace.addView({\n    \"organizedBy\": rootFolder.views,\n    \"browseName\": \"Line4 Conveyor DINTs\"\n  });\n\n  const viewFloats = namespace.addView({\n    \"organizedBy\": rootFolder.views,\n    \"browseName\": \"Line4 Conveyor Floats\"\n  });\n\n  viewBools.addReference({\n    \"referenceType\": \"Organizes\",\n    \"nodeId\": Conveyor_RTS.nodeId\n  });\n\n  viewBools.addReference({\n    \"referenceType\": \"Organizes\",\n    \"nodeId\": Robot_RTS.nodeId\n  });\n\n  viewBools.addReference({\n    \"referenceType\": \"Organizes\",\n    \"nodeId\": Conveyor_Running.nodeId\n  });\n\n  viewBools.addReference({\n    \"referenceType\": \"Organizes\",\n    \"nodeId\": Line4_Fault.nodeId\n  });\n\n\n  viewDINTs.addReference({\n    \"referenceType\": \"Organizes\",\n    \"nodeId\": Line4_State.nodeId\n  });\n\n  viewFloats.addReference({\n    \"referenceType\": \"Organizes\",\n    \"nodeId\": Robot_Position.nodeId\n  });\n",[191,2700,2701,2716,2722,2731,2735,2739,2754,2760,2769,2773,2777,2792,2798,2807,2811,2815,2824,2834,2841,2845,2849,2857,2867,2874,2878,2882,2890,2900,2907,2911,2915,2923,2933,2940,2944,2948,2952,2961,2971,2978,2982,2986,2995,3005,3012],{"__ignoreMap":12},[697,2702,2703,2705,2708,2710,2712,2714],{"class":699,"line":700},[697,2704,999],{"class":743},[697,2706,2707],{"class":703}," viewBools",[697,2709,1005],{"class":743},[697,2711,1008],{"class":707},[697,2713,2632],{"class":711},[697,2715,1318],{"class":707},[697,2717,2718,2720],{"class":699,"line":730},[697,2719,1323],{"class":717},[697,2721,2641],{"class":707},[697,2723,2724,2726,2728],{"class":699,"line":750},[697,2725,1019],{"class":717},[697,2727,1022],{"class":707},[697,2729,2730],{"class":717},"\"Line 4 Conveyor Bools\"\n",[697,2732,2733],{"class":699,"line":761},[697,2734,1030],{"class":707},[697,2736,2737],{"class":699,"line":770},[697,2738,1649],{"emptyLinePlaceholder":426},[697,2740,2741,2743,2746,2748,2750,2752],{"class":699,"line":788},[697,2742,999],{"class":743},[697,2744,2745],{"class":703}," viewDINTs",[697,2747,1005],{"class":743},[697,2749,1008],{"class":707},[697,2751,2632],{"class":711},[697,2753,1318],{"class":707},[697,2755,2756,2758],{"class":699,"line":794},[697,2757,1323],{"class":717},[697,2759,2641],{"class":707},[697,2761,2762,2764,2766],{"class":699,"line":805},[697,2763,1019],{"class":717},[697,2765,1022],{"class":707},[697,2767,2768],{"class":717},"\"Line4 Conveyor DINTs\"\n",[697,2770,2771],{"class":699,"line":825},[697,2772,1030],{"class":707},[697,2774,2775],{"class":699,"line":831},[697,2776,1649],{"emptyLinePlaceholder":426},[697,2778,2779,2781,2784,2786,2788,2790],{"class":699,"line":1229},[697,2780,999],{"class":743},[697,2782,2783],{"class":703}," viewFloats",[697,2785,1005],{"class":743},[697,2787,1008],{"class":707},[697,2789,2632],{"class":711},[697,2791,1318],{"class":707},[697,2793,2794,2796],{"class":699,"line":1239},[697,2795,1323],{"class":717},[697,2797,2641],{"class":707},[697,2799,2800,2802,2804],{"class":699,"line":1244},[697,2801,1019],{"class":717},[697,2803,1022],{"class":707},[697,2805,2806],{"class":717},"\"Line4 Conveyor Floats\"\n",[697,2808,2809],{"class":699,"line":1260},[697,2810,1030],{"class":707},[697,2812,2813],{"class":699,"line":1270},[697,2814,1649],{"emptyLinePlaceholder":426},[697,2816,2817,2820,2822],{"class":699,"line":1443},[697,2818,2819],{"class":707},"  viewBools.",[697,2821,2667],{"class":711},[697,2823,1318],{"class":707},[697,2825,2826,2828,2830,2832],{"class":699,"line":1613},[697,2827,2674],{"class":717},[697,2829,1022],{"class":707},[697,2831,2679],{"class":717},[697,2833,767],{"class":707},[697,2835,2836,2838],{"class":699,"line":1619},[697,2837,1342],{"class":717},[697,2839,2840],{"class":707},": Conveyor_RTS.nodeId\n",[697,2842,2843],{"class":699,"line":1624},[697,2844,1030],{"class":707},[697,2846,2847],{"class":699,"line":1631},[697,2848,1649],{"emptyLinePlaceholder":426},[697,2850,2851,2853,2855],{"class":699,"line":1636},[697,2852,2819],{"class":707},[697,2854,2667],{"class":711},[697,2856,1318],{"class":707},[697,2858,2859,2861,2863,2865],{"class":699,"line":1641},[697,2860,2674],{"class":717},[697,2862,1022],{"class":707},[697,2864,2679],{"class":717},[697,2866,767],{"class":707},[697,2868,2869,2871],{"class":699,"line":1646},[697,2870,1342],{"class":717},[697,2872,2873],{"class":707},": Robot_RTS.nodeId\n",[697,2875,2876],{"class":699,"line":1652},[697,2877,1030],{"class":707},[697,2879,2880],{"class":699,"line":1668},[697,2881,1649],{"emptyLinePlaceholder":426},[697,2883,2884,2886,2888],{"class":699,"line":1675},[697,2885,2819],{"class":707},[697,2887,2667],{"class":711},[697,2889,1318],{"class":707},[697,2891,2892,2894,2896,2898],{"class":699,"line":1687},[697,2893,2674],{"class":717},[697,2895,1022],{"class":707},[697,2897,2679],{"class":717},[697,2899,767],{"class":707},[697,2901,2902,2904],{"class":699,"line":1699},[697,2903,1342],{"class":717},[697,2905,2906],{"class":707},": Conveyor_Running.nodeId\n",[697,2908,2909],{"class":699,"line":1710},[697,2910,1030],{"class":707},[697,2912,2913],{"class":699,"line":1717},[697,2914,1649],{"emptyLinePlaceholder":426},[697,2916,2917,2919,2921],{"class":699,"line":1728},[697,2918,2819],{"class":707},[697,2920,2667],{"class":711},[697,2922,1318],{"class":707},[697,2924,2925,2927,2929,2931],{"class":699,"line":1739},[697,2926,2674],{"class":717},[697,2928,1022],{"class":707},[697,2930,2679],{"class":717},[697,2932,767],{"class":707},[697,2934,2935,2937],{"class":699,"line":1746},[697,2936,1342],{"class":717},[697,2938,2939],{"class":707},": Line4_Fault.nodeId\n",[697,2941,2942],{"class":699,"line":1761},[697,2943,1030],{"class":707},[697,2945,2946],{"class":699,"line":1766},[697,2947,1649],{"emptyLinePlaceholder":426},[697,2949,2950],{"class":699,"line":1771},[697,2951,1649],{"emptyLinePlaceholder":426},[697,2953,2954,2957,2959],{"class":699,"line":1786},[697,2955,2956],{"class":707},"  viewDINTs.",[697,2958,2667],{"class":711},[697,2960,1318],{"class":707},[697,2962,2963,2965,2967,2969],{"class":699,"line":1795},[697,2964,2674],{"class":717},[697,2966,1022],{"class":707},[697,2968,2679],{"class":717},[697,2970,767],{"class":707},[697,2972,2973,2975],{"class":699,"line":1803},[697,2974,1342],{"class":717},[697,2976,2977],{"class":707},": Line4_State.nodeId\n",[697,2979,2980],{"class":699,"line":1808},[697,2981,1030],{"class":707},[697,2983,2984],{"class":699,"line":1813},[697,2985,1649],{"emptyLinePlaceholder":426},[697,2987,2988,2991,2993],{"class":699,"line":1820},[697,2989,2990],{"class":707},"  viewFloats.",[697,2992,2667],{"class":711},[697,2994,1318],{"class":707},[697,2996,2997,2999,3001,3003],{"class":699,"line":1825},[697,2998,2674],{"class":717},[697,3000,1022],{"class":707},[697,3002,2679],{"class":717},[697,3004,767],{"class":707},[697,3006,3007,3009],{"class":699,"line":1830},[697,3008,1342],{"class":717},[697,3010,3011],{"class":707},": Robot_Position.nodeId\n",[697,3013,3014],{"class":699,"line":1835},[697,3015,1030],{"class":707},[18,3017,3018],{},"We’ve now completed the address space, so all that’s left is to define the OPC UA endpoint.",[31,3020,3022],{"id":3021},"wrap-up-server-configuration","Wrap Up Server Configuration",[18,3024,3025],{},"Go to the discovery tab and define an endpoint following the convention below, with the ip address matching the ip address of your Node-RED instance.",[18,3027,3028],{},[48,3029],{"alt":3030,"src":3031},"image-20230718-155245.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fimage-20230718-155245.png",[18,3033,3034],{},"Now, our OPC UA Server is set up and ready to be browsable by an OPC UA Client.",[18,3036,3037,3038,3040,3041,29],{},"Deploy the changes and make sure the ",[191,3039,613],{}," is reporting ",[191,3042,3043],{},"active",[18,3045,3046,3050],{},[48,3047],{"alt":3048,"src":3049},"compact-server-active.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fcompact-server-active.png","\nIf not, go back and check your code for errors.  The Node-RED logfiles will come in handy to track down issues if your server isn’t activating.",[18,3052,3053],{},"This wraps up the 4th objective of our application.",[18,3055,3056],{},[48,3057],{"alt":3058,"src":3059},"PLC-Information-Model-4-of-6-1.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002FPLC-Information-Model-4-of-6-1.png",[18,3061,3062,427,3064,3066,433,3068,3070,568,3072,3074,3076,3077],{},[423,3063],{"type":425,"checked":426},[429,3065],{},[423,3067],{"type":425,"checked":426},[429,3069],{},[423,3071],{"type":425,"checked":426},[429,3073],{},[423,3075],{"type":425,"checked":426}," Program the OPC Server address space",[429,3078],{},[31,3080,3082],{"id":3081},"security-optional","Security (Optional)",[18,3084,3085],{},"At this point, our OPC UA Server will accept a client connection, but it won’t be secure, so we should take the extra step and encrypt our OPC UA traffic.",[18,3087,3088,3089,3092,3093,3095],{},"To do this, go to the ",[191,3090,3091],{},"Security"," tab of the ",[191,3094,613],{}," properties.",[18,3097,3098,3102,3103,3106,3107,29],{},[48,3099],{"alt":3100,"src":3101},"security-tab-default.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fsecurity-tab-default.png","\nBy default, the server is using no security, and allowing anonymous connections.  Let’s fix that by unchecking ",[191,3104,3105],{},"Allow Anonymous"," , and checking ",[191,3108,3109],{},"Use invididual Certificate Files",[18,3111,3112,3116,3117,3119],{},[48,3113],{"alt":3114,"src":3115},"individual-cert-file-option.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Findividual-cert-file-option.png","\nThe node gives us some clues on how we can populate this section.  When ",[191,3118,197],{}," was installed, it created self-signed ssl certificates that are bound to our host system.  Let’s make use of them.",[18,3121,3122,3123,3126],{},"navigate to ",[191,3124,3125],{},".\u002Fnode-red-contrib-opcua-server\u002Fcertificates"," directory, where the node-red instance has installed the Node-RED module.",[207,3128,3129,3135],{},[56,3130,3131,3132],{},"I have Node-RED installed in the root path of my server, so my full path to the certs folder is ",[191,3133,3134],{},"\u002Froot\u002F.node-red\u002Fnode_modules\u002Fnode-red-contrib-opcua-server\u002Fcertificates",[56,3136,3137,3138,29],{},"If you’re having trouble finding the directory, do a search for the file ",[191,3139,3140],{},"server_selfsigned_cert_2048.pem",[18,3142,3143],{},"Once you’ve navigated to the correct directory, it should be full of various cert files.",[18,3145,3146,3150,3151,3153,3154,3157],{},[48,3147],{"alt":3148,"src":3149},"cert-list.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fcert-list.png","\nThe two cert files we care about, which were already pre-defined in the node, are ",[191,3152,3140],{},", which is the public cert file, and ",[191,3155,3156],{},"server_key_2048.pem",", which is the private cert file.",[18,3159,3160,3161,3163,3164,3167],{},"Go back to the node configuration and populate the ",[191,3162,3091],{}," tab with the ",[83,3165,3166],{},"full absolute path"," to the files.",[18,3169,3170,3174,3175,3177],{},[48,3171],{"alt":3172,"src":3173},"cert-tab-filled.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fcert-tab-filled.png","\nHit done and redeploy the node.  Make sure the server reports ",[191,3176,3043],{},".  If not, check the cert paths and try again.",[18,3179,3180],{},"You may also run into file permission issues, depending on how you set up your Node-RED instance, so make sure Node-RED also has read access to the files.",[18,3182,3183],{},"We’re not done yet.  The server is happy, but the OPC Client will need access to these cert files as well.  So copy the files to a location that will make the two cert files accessible to the OPC Client.  In my case, the OPC Client is being ran on a personal Windows machine on the same network.  So I copied the cert files to a nas, which both my Node-RED instance and my Windows machine have access to.",[18,3185,3186,3190,3194],{},[48,3187],{"alt":3188,"src":3189},"copy-certs.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fcopy-certs.png",[48,3191],{"alt":3192,"src":3193},"copied-certs.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fcopied-certs.png","\nNow we can move on to OPC Client Configuration.  We’ve achieved 5 out of 6 objectives.",[18,3196,3197],{},[48,3198],{"alt":3199,"src":3200},"PLC-Information-Model-5-of-6-1.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002FPLC-Information-Model-5-of-6-1.png",[18,3202,3203,427,3205,3207,433,3209,3211,568,3213,3215,3076,3217,3219,3221,3222],{},[423,3204],{"type":425,"checked":426},[429,3206],{},[423,3208],{"type":425,"checked":426},[429,3210],{},[423,3212],{"type":425,"checked":426},[429,3214],{},[423,3216],{"type":425,"checked":426},[429,3218],{},[423,3220],{"type":425,"checked":426}," Encrypt the OPC Server with SSL",[429,3223],{},[31,3225,3227],{"id":3226},"opc-ua-client-configuration-and-testing","OPC UA Client Configuration and Testing",[18,3229,3230],{},"To connect to our Node-RED OPC server, enter the endpoint url and press “connect to server”.",[18,3232,3233,3237],{},[48,3234],{"alt":3235,"src":3236},"opc-client-connect.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fopc-client-connect.png","\nSecurity settings are displayed.",[18,3239,3240,3241,3244,3245],{},"We’re going to select ",[191,3242,3243],{},"Sign & Encrypt"," and change the security policy to ",[191,3246,3247],{},"Aes128Sha256RsaOaep",[18,3249,3250,3254],{},[48,3251],{"alt":3252,"src":3253},"sign&encrypt.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fsign&encrypt.png","\nWhen we try to connect, our connection to the server is rejected, because we haven’t pointed the client to our ssl cert files.  Press Okay to acknowledge the error and we can fix that problem.",[18,3256,3257,3261,3262,3265,3266,3269],{},[48,3258],{"alt":3259,"src":3260},"connect-rejected.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fconnect-rejected.png","\nWhen you acknowledge the connection error, you are taken to the ",[191,3263,3264],{},"User Authentication"," properties.  Select ",[191,3267,3268],{},"Certificate and Private key",".  We need to point to our certificate and private key files.",[18,3271,3272,3276,3277,3280],{},[48,3273],{"alt":3274,"src":3275},"client-cert-path.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fclient-cert-path.png","\nWhen we browse for our certificate file, the client software tells us it’s expecting a ",[191,3278,3279],{},"*.der"," file, which we don’t have yet.",[18,3282,3283,3287,3288,29],{},[48,3284],{"alt":3285,"src":3286},"der-file.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fder-file.png","\nHowever, we can create one from our existing cert file using ",[191,3289,3290],{},"openssl",[18,3292,3293,3294,29],{},"If you don’t already have openssl installed, ",[25,3295,3298],{"href":3296,"rel":3297},"https:\u002F\u002Fwww.openssl.org\u002F",[219],"install it",[18,3300,3301],{},"Then from a command prompt, run the following command in the directory where your client-side cert files are stored -",[689,3303,3308],{"className":3304,"code":3306,"language":3307},[3305],"language-text","openssl x509 -in server_selfsigned_cert_2048.pem -out server_selfsigned_cert_2048.der -outform DER\n","text",[191,3309,3306],{"__ignoreMap":12},[18,3311,3312,3316],{},[48,3313],{"alt":3314,"src":3315},"ssl-pub-keygen.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fssl-pub-keygen.png","\nThis command will generate the .der file the opc client is expecting to see.",[18,3318,3319,3323,3324,3327,3328,3330],{},[48,3320],{"alt":3321,"src":3322},"copied-certs-with-pubkey.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fcopied-certs-with-pubkey.png","\nNow we can go back and point to the public key file, which is the ",[191,3325,3326],{},"server_selfsigned_cert_2048.der"," file, and the private key file, which is the ",[191,3329,3156],{}," file.",[18,3332,3333,3337],{},[48,3334],{"alt":3335,"src":3336},"client-cert-path-filled.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fclient-cert-path-filled.png","\nThe first time you do this, you will be asked to accept the server certificate.",[18,3339,3340,3344],{},[48,3341],{"alt":3342,"src":3343},"opc-client-accept-cert.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fopc-client-accept-cert.png","\nIf you choose accept permanently, you won’t see this prompt again.  You should now have access to browse the OPC Server.",[18,3346,3347],{},"As can be seen, our OPC Client sees the data from our PLC matching our OPC Information Model we defined in our Node-RED server address space.",[18,3349,3350,3354],{},[48,3351],{"alt":3352,"src":3353},"image-20230718-164326.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fimage-20230718-164326.png","\nWe’ve now achieved all of our design objectives.",[18,3356,3357],{},[48,3358],{"alt":3359,"src":3360},"PLC-Information-Model-6-of-6-1.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002FPLC-Information-Model-6-of-6-1.png",[18,3362,3363,427,3365,3367,433,3369,3371,568,3373,3375,3076,3377,3379,3221,3381,3383,3385,3386],{},[423,3364],{"type":425,"checked":426},[429,3366],{},[423,3368],{"type":425,"checked":426},[429,3370],{},[423,3372],{"type":425,"checked":426},[429,3374],{},[423,3376],{"type":425,"checked":426},[429,3378],{},[423,3380],{"type":425,"checked":426},[429,3382],{},[423,3384],{"type":425,"checked":426}," Set up the OPC Client",[429,3387],{},[18,3389,3390],{},"Our custom OPC UA application is complete and production-ready.",[18,3392,3393],{},"Test the application by confirming values changed in the PLC are reflected in the OPC UA Client.",[18,3395,3396,3397,3400],{},"In my PLC code, I created a sine wave generator that changes the ",[191,3398,3399],{},"Robot Axis A1 Position"," value continuously, so the value is always changing, making it easy to confirm that the server is passing OPC traffic correctly.",[18,3402,3403],{},[48,3404],{"alt":3405,"src":3406},"sine-wave-gen.png","\u002Fnode-red-media\u002Fprotocol\u002Fimages\u002Fsine-wave-gen.png",[18,3408,3409],{},"In this documentation, we covered in detail how to create an OPC UA application that pulls data from an Allen Bradley PLC over Ethernet\u002FIP, store the PLC data in Node-RED context memory, then publish the PLC data from context memory onto a ssl secured OPC UA Server.  An OPC Client can subscribe to the OPC UA Server over an encrypted connection, making the application deployable in a production environment, including in the cloud if desired.",[18,3411,3412,3413,3418],{},"With the foundation laid in this documentation, you can customize the application to fit your individual needs, with an understanding of what is going on “under the hood” of an OPC UA Server.  This application only scratches the surface of what features OPC UA has available.  Refer to the ",[25,3414,3417],{"href":3415,"rel":3416},"https:\u002F\u002Fnode-opcua.github.io\u002F",[219],"NodeOPCUA sdk"," and experiment by building on top of this example if you desire to learn more or want to add features this application is lacking.",[18,3420,3421],{},"In the next documentation of the OPC UA series, we will establish how to create an OPC UA Client application in Node-RED.",[18,3423,3424,3425,29],{},"OPC UA is one of several industrial protocols FlowFuse uses to connect PLCs to the modern stack. For EtherNet\u002FIP, Siemens S7, Modbus, MQTT, and more, see the ",[25,3426,3428],{"href":3427},"\u002Flanding\u002Fplc\u002F","FlowFuse PLC integration overview",[18,3430,3431],{},"Source code for flow used in this documentation -",[3433,3434],"render-flow",{":height":3435,"flow":3436},"200","W3siaWQiOiIyZThjN2Y1Yy5hYjczZCIsInR5cGUiOiJ0YWIiLCJsYWJlbCI6Ik9QQy1VQSBDdXN0b20gQ29udGV4dCBTZXJ2ZXIiLCJkaXNhYmxlZCI6ZmFsc2UsImluZm8iOiIifSx7ImlkIjoiMzhjZTEwZGUuN2Q4YyIsInR5cGUiOiJvcGN1YS1jb21wYWN0LXNlcnZlciIsInoiOiIyZThjN2Y1Yy5hYjczZCIsInBvcnQiOiI1NDg0NSIsImVuZHBvaW50IjoiIiwicHJvZHVjdFVyaSI6IiIsImFjY2VwdEV4dGVybmFsQ29tbWFuZHMiOnRydWUsIm1heEFsbG93ZWRTZXNzaW9uTnVtYmVyIjoiMTAiLCJtYXhDb25uZWN0aW9uc1BlckVuZHBvaW50IjoiMTAiLCJtYXhBbGxvd2VkU3Vic2NyaXB0aW9uTnVtYmVyIjoiMTAwIiwiYWx0ZXJuYXRlSG9zdG5hbWUiOiIiLCJuYW1lIjoiIiwic2hvd1N0YXR1c0FjdGl2aXRpZXMiOmZhbHNlLCJzaG93RXJyb3JzIjp0cnVlLCJhbGxvd0Fub255bW91cyI6ZmFsc2UsImluZGl2aWR1YWxDZXJ0cyI6dHJ1ZSwiaXNBdWRpdGluZyI6ZmFsc2UsInNlcnZlckRpc2NvdmVyeSI6dHJ1ZSwidXNlcnMiOltdLCJ4bWxzZXRzT1BDVUEiOltdLCJwdWJsaWNDZXJ0aWZpY2F0ZUZpbGUiOiIvcm9vdC8ubm9kZS1yZWQvbm9kZV9tb2R1bGVzL25vZGUtcmVkLWNvbnRyaWItb3BjdWEtc2VydmVyL2NlcnRpZmljYXRlcy9zZXJ2ZXJfc2VsZnNpZ25lZF9jZXJ0XzIwNDgucGVtIiwicHJpdmF0ZUNlcnRpZmljYXRlRmlsZSI6Ii9yb290Ly5ub2RlLXJlZC9ub2RlX21vZHVsZXMvbm9kZS1yZWQtY29udHJpYi1vcGN1YS1zZXJ2ZXIvY2VydGlmaWNhdGVzL3NlcnZlcl9rZXlfMjA0OC5wZW0iLCJyZWdpc3RlclNlcnZlck1ldGhvZCI6IjEiLCJkaXNjb3ZlcnlTZXJ2ZXJFbmRwb2ludFVybCI6Im9wYy50Y3A6Ly8xOTIuMTY4LjAuMTE0OjU0ODQ1IiwiY2FwYWJpbGl0aWVzRm9yTUROUyI6IiIsIm1heE5vZGVzUGVyUmVhZCI6MTAwMCwibWF4Tm9kZXNQZXJXcml0ZSI6MTAwMCwibWF4Tm9kZXNQZXJIaXN0b3J5UmVhZERhdGEiOjEwMCwibWF4Tm9kZXNQZXJCcm93c2UiOjMwMDAsIm1heEJyb3dzZUNvbnRpbnVhdGlvblBvaW50cyI6IjEwIiwibWF4SGlzdG9yeUNvbnRpbnVhdGlvblBvaW50cyI6IjEwIiwiZGVsYXlUb0luaXQiOiIxMDAwIiwiZGVsYXlUb0Nsb3NlIjoiMjAwIiwic2VydmVyU2h1dGRvd25UaW1lb3V0IjoiMTAwIiwiYWRkcmVzc1NwYWNlU2NyaXB0IjoiZnVuY3Rpb24gY29uc3RydWN0QWxhcm1BZGRyZXNzU3BhY2Uoc2VydmVyLCBhZGRyZXNzU3BhY2UsIGV2ZW50T2JqZWN0cywgZG9uZSkge1xuICAvLyBzZXJ2ZXIgPSB0aGUgY3JlYXRlZCBub2RlLW9wY3VhIHNlcnZlclxuICAvLyBhZGRyZXNzU3BhY2UgPSBhZGRyZXNzIHNwYWNlIG9mIHRoZSBub2RlLW9wY3VhIHNlcnZlclxuICAvLyBldmVudE9iamVjdHMgPSBhZGQgZXZlbnQgdmFyaWFibGVzIGhlcmUgdG8gaG9sZCB0aGVtIGluIG1lbW9yeSBmcm9tIHRoaXMgc2NyaXB0XG5cbiAgLy8gaW50ZXJuYWwgc2FuZGJveCBvYmplY3RzIGFyZTpcbiAgLy8gbm9kZSA9IHRoZSBjb21wYWN0IHNlcnZlciBub2RlLFxuICAvLyBjb3JlU2VydmVyID0gY29yZSBjb21wYWN0IHNlcnZlciBvYmplY3QgZm9yIGRlYnVnIGFuZCBhY2Nlc3MgdG8gTm9kZU9QQ1VBXG4gIC8vIHRoaXMuc2FuZGJveE5vZGVDb250ZXh0ID0gbm9kZSBjb250ZXh0IG5vZGUtcmVkXG4gIC8vIHRoaXMuc2FuZGJveEZsb3dDb250ZXh0ID0gZmxvdyBjb250ZXh0IG5vZGUtcmVkXG4gIC8vIHRoaXMuc2FuZGJveEdsb2JhbENvbnRleHQgPSBnbG9iYWwgY29udGV4dCBub2RlLXJlZFxuICAvLyB0aGlzLnNhbmRib3hFbnYgPSBlbnYgdmFyaWFibGVzXG4gIC8vIHRpbWVvdXQgYW5kIGludGVydmFsIGZ1bmN0aW9ucyBhcyBleHBlY3RlZCBmcm9tIG5vZGVqc1xuXG4gIGNvbnN0IG9wY3VhID0gY29yZVNlcnZlci5jaG9yZUNvbXBhY3Qub3BjdWE7XG4gIGNvbnN0IExvY2FsaXplZFRleHQgPSBvcGN1YS5Mb2NhbGl6ZWRUZXh0O1xuICBjb25zdCBuYW1lc3BhY2UgPSBhZGRyZXNzU3BhY2UuZ2V0T3duTmFtZXNwYWNlKCk7XG5cbiAgY29uc3QgVmFyaWFudCA9IG9wY3VhLlZhcmlhbnQ7XG4gIGNvbnN0IERhdGFUeXBlID0gb3BjdWEuRGF0YVR5cGU7XG4gIGNvbnN0IERhdGFWYWx1ZSA9IG9wY3VhLkRhdGFWYWx1ZTtcblxuICB2YXIgZmxleFNlcnZlckludGVybmFscyA9IHRoaXM7XG5cbiAgdGhpcy5zYW5kYm94Rmxvd0NvbnRleHQuc2V0KFwiY29udmV5b3JEYXRhLkNvbnZleW9yX1JUU1wiLCBmYWxzZSk7XG4gIHRoaXMuc2FuZGJveEZsb3dDb250ZXh0LnNldChcImNvbnZleW9yRGF0YS5Sb2JvdF9SVFNcIiwgZmFsc2UpO1xuICB0aGlzLnNhbmRib3hGbG93Q29udGV4dC5zZXQoXCJjb252ZXlvckRhdGEuUm9ib3RfUG9zaXRpb25cIiwgMCk7XG4gIHRoaXMuc2FuZGJveEZsb3dDb250ZXh0LnNldChcImNvbnZleW9yRGF0YS5Db252ZXlvcl9SdW5uaW5nXCIsIGZhbHNlKTtcbiAgdGhpcy5zYW5kYm94Rmxvd0NvbnRleHQuc2V0KFwiY29udmV5b3JEYXRhLkxpbmU0X1N0YXRlXCIsIDApO1xuICB0aGlzLnNhbmRib3hGbG93Q29udGV4dC5zZXQoXCJjb252ZXlvckRhdGEuTGluZTRfRmF1bHRcIiwgZmFsc2UpO1xuXG4gIC8vIHRoaXMuc2FuZGJveEZsb3dDb250ZXh0LnNldChcImlzb0lucHV0MVwiLCAwKTtcbiAgLy8gdGhpcy5zZXRJbnRlcnZhbCgoKSA9PiB7XG4gIC8vICAgZmxleFNlcnZlckludGVybmFscy5zYW5kYm94Rmxvd0NvbnRleHQuc2V0KFxuICAvLyAgICAgXCJpc29JbnB1dDFcIixcbiAgLy8gICAgIE1hdGgucmFuZG9tKCkgKyA1MC4wXG4gIC8vICAgKTtcbiAgLy8gfSwgNTAwKTtcbiAgLy8gdGhpcy5zYW5kYm94Rmxvd0NvbnRleHQuc2V0KFwiaXNvSW5wdXQyXCIsIDApO1xuICAvLyB0aGlzLnNhbmRib3hGbG93Q29udGV4dC5zZXQoXCJpc29JbnB1dDNcIiwgMCk7XG4gIC8vIHRoaXMuc2FuZGJveEZsb3dDb250ZXh0LnNldChcImlzb0lucHV0NFwiLCAwKTtcbiAgLy8gdGhpcy5zYW5kYm94Rmxvd0NvbnRleHQuc2V0KFwiaXNvSW5wdXQ1XCIsIDApO1xuICAvLyB0aGlzLnNhbmRib3hGbG93Q29udGV4dC5zZXQoXCJpc29JbnB1dDZcIiwgMCk7XG4gIC8vIHRoaXMuc2FuZGJveEZsb3dDb250ZXh0LnNldChcImlzb0lucHV0N1wiLCAwKTtcbiAgLy8gdGhpcy5zYW5kYm94Rmxvd0NvbnRleHQuc2V0KFwiaXNvSW5wdXQ4XCIsIDApO1xuICAvLyB0aGlzLnNhbmRib3hGbG93Q29udGV4dC5zZXQoXCJpc29PdXRwdXQxXCIsIDApO1xuICAvLyB0aGlzLnNldEludGVydmFsKCgpID0+IHtcbiAgLy8gICBmbGV4U2VydmVySW50ZXJuYWxzLnNhbmRib3hGbG93Q29udGV4dC5zZXQoXG4gIC8vICAgICBcImlzb091dHB1dDFcIixcbiAgLy8gICAgIE1hdGgucmFuZG9tKCkgKyAxMC4wXG4gIC8vICAgKTtcbiAgLy8gfSwgNTAwKTtcblxuICAvLyB0aGlzLnNhbmRib3hGbG93Q29udGV4dC5zZXQoXCJpc29PdXRwdXQyXCIsIDApO1xuICAvLyB0aGlzLnNhbmRib3hGbG93Q29udGV4dC5zZXQoXCJpc29PdXRwdXQzXCIsIDApO1xuICAvLyB0aGlzLnNhbmRib3hGbG93Q29udGV4dC5zZXQoXCJpc29PdXRwdXQ0XCIsIDApO1xuICAvLyB0aGlzLnNhbmRib3hGbG93Q29udGV4dC5zZXQoXCJpc29PdXRwdXQ1XCIsIDApO1xuICAvLyB0aGlzLnNhbmRib3hGbG93Q29udGV4dC5zZXQoXCJpc29PdXRwdXQ2XCIsIDApO1xuICAvLyB0aGlzLnNhbmRib3hGbG93Q29udGV4dC5zZXQoXCJpc29PdXRwdXQ3XCIsIDApO1xuICAvLyB0aGlzLnNhbmRib3hGbG93Q29udGV4dC5zZXQoXCJpc29PdXRwdXQ4XCIsIDApO1xuXG4gIGNvcmVTZXJ2ZXIuZGVidWdMb2coXCJpbml0IGR5bmFtaWMgYWRkcmVzcyBzcGFjZVwiKTtcbiAgY29uc3Qgcm9vdEZvbGRlciA9IGFkZHJlc3NTcGFjZS5maW5kTm9kZShcIlJvb3RGb2xkZXJcIik7XG5cbiAgbm9kZS53YXJuKFwiY29uc3RydWN0IG5ldyBhZGRyZXNzIHNwYWNlIGZvciBPUEMgVUFcIik7XG5cbiAgY29uc3QgbXlEZXZpY2UgPSBuYW1lc3BhY2UuYWRkRm9sZGVyKHJvb3RGb2xkZXIub2JqZWN0cywge1xuICAgIFwiYnJvd3NlTmFtZVwiOiBcIkxpbmUgNCBQTENcIlxuICB9KTtcbiAgY29uc3QgY29udmV5b3JGb2xkZXIgPSBuYW1lc3BhY2UuYWRkRm9sZGVyKG15RGV2aWNlLCB7IFwiYnJvd3NlTmFtZVwiOiBcIkNvbnZleW9yXCIgfSk7XG4gIGNvbnN0IGNvbnZleW9yQm9vbHMgPSBuYW1lc3BhY2UuYWRkRm9sZGVyKGNvbnZleW9yRm9sZGVyLCB7XG4gICAgXCJicm93c2VOYW1lXCI6IFwiQm9vbHNcIlxuICB9KTtcbiAgY29uc3QgY29udmV5b3JESU5UcyA9IG5hbWVzcGFjZS5hZGRGb2xkZXIoY29udmV5b3JGb2xkZXIsIHtcbiAgICBcImJyb3dzZU5hbWVcIjogXCJESU5Uc1wiXG4gIH0pO1xuICBjb25zdCBjb252ZXlvckZsb2F0cyA9IG5hbWVzcGFjZS5hZGRGb2xkZXIoY29udmV5b3JGb2xkZXIsIHtcbiAgICBcImJyb3dzZU5hbWVcIjogXCJGbG9hdHNcIlxuICB9KTtcblxuICAvLyBDb25zdHJ1Y3QgTm9kZXNcbiAgY29uc3QgQ29udmV5b3JfUlRTID0gbmFtZXNwYWNlLmFkZFZhcmlhYmxlKHtcbiAgICBcIm9yZ2FuaXplZEJ5XCI6IGNvbnZleW9yQm9vbHMsXG4gICAgXCJicm93c2VOYW1lXCI6IFwiQ29udmV5b3IgUmVhZHkgdG8gU3RhcnRcIixcbiAgICBcIm5vZGVJZFwiOiBcIm5zPTE7cz1Db252ZXlvcl9SVFNcIixcbiAgICBcImRhdGFUeXBlXCI6IFwiQm9vbGVhblwiLFxuICAgIFwidmFsdWVcIjoge1xuICAgICAgXCJnZXRcIjogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gbmV3IFZhcmlhbnQoe1xuICAgICAgICAgIFwiZGF0YVR5cGVcIjogRGF0YVR5cGUuQm9vbGVhbixcbiAgICAgICAgICBcInZhbHVlXCI6IGZsZXhTZXJ2ZXJJbnRlcm5hbHMuc2FuZGJveEZsb3dDb250ZXh0LmdldChcImNvbnZleW9yRGF0YS5Db252ZXlvcl9SVFNcIilcbiAgICAgICAgfSk7XG4gICAgICB9LFxuICAgICAgXCJzZXRcIjogZnVuY3Rpb24gKHZhcmlhbnQpIHtcbiAgICAgICAgZmxleFNlcnZlckludGVybmFscy5zYW5kYm94Rmxvd0NvbnRleHQuc2V0KFxuICAgICAgICAgIFwiY29udmV5b3JEYXRhLkNvbnZleW9yX1JUU1wiLFxuICAgICAgICAgIHZhcmlhbnQudmFsdWVcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuIG9wY3VhLlN0YXR1c0NvZGVzLkdvb2Q7XG4gICAgICB9XG4gICAgfVxuICB9KTtcblxuICBjb25zdCBSb2JvdF9SVFMgPSBuYW1lc3BhY2UuYWRkVmFyaWFibGUoe1xuICAgIFwib3JnYW5pemVkQnlcIjogY29udmV5b3JCb29scyxcbiAgICBcImJyb3dzZU5hbWVcIjogXCJSb2JvdCBSZWFkeSB0byBTdGFydFwiLFxuICAgIFwibm9kZUlkXCI6IFwibnM9MTtzPVJvYm90X1JUU1wiLFxuICAgIFwiZGF0YVR5cGVcIjogXCJCb29sZWFuXCIsXG4gICAgXCJ2YWx1ZVwiOiB7XG4gICAgICBcImdldFwiOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBuZXcgVmFyaWFudCh7XG4gICAgICAgICAgXCJkYXRhVHlwZVwiOiBEYXRhVHlwZS5Cb29sZWFuLFxuICAgICAgICAgIFwidmFsdWVcIjogZmxleFNlcnZlckludGVybmFscy5zYW5kYm94Rmxvd0NvbnRleHQuZ2V0KFwiY29udmV5b3JEYXRhLlJvYm90X1JUU1wiKVxuICAgICAgICB9KTtcbiAgICAgIH0sXG4gICAgICBcInNldFwiOiBmdW5jdGlvbiAodmFyaWFudCkge1xuICAgICAgICBmbGV4U2VydmVySW50ZXJuYWxzLnNhbmRib3hGbG93Q29udGV4dC5zZXQoXG4gICAgICAgICAgXCJjb252ZXlvckRhdGEuUm9ib3RfUlRTXCIsXG4gICAgICAgICAgdmFyaWFudC52YWx1ZVxuICAgICAgICApO1xuICAgICAgICByZXR1cm4gb3BjdWEuU3RhdHVzQ29kZXMuR29vZDtcbiAgICAgIH1cbiAgICB9XG4gIH0pO1xuXG4gIGNvbnN0IENvbnZleW9yX1J1bm5pbmcgPSBuYW1lc3BhY2UuYWRkVmFyaWFibGUoe1xuICAgIFwib3JnYW5pemVkQnlcIjogY29udmV5b3JCb29scyxcbiAgICBcImJyb3dzZU5hbWVcIjogXCJDb252ZXlvciBSdW5uaW5nXCIsXG4gICAgXCJub2RlSWRcIjogXCJucz0xO3M9Q29udmV5b3JfUnVubmluZ1wiLFxuICAgIFwiZGF0YVR5cGVcIjogXCJCb29sZWFuXCIsXG4gICAgXCJ2YWx1ZVwiOiB7XG4gICAgICBcImdldFwiOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBuZXcgVmFyaWFudCh7XG4gICAgICAgICAgXCJkYXRhVHlwZVwiOiBEYXRhVHlwZS5Cb29sZWFuLFxuICAgICAgICAgIFwidmFsdWVcIjogZmxleFNlcnZlckludGVybmFscy5zYW5kYm94Rmxvd0NvbnRleHQuZ2V0KFwiY29udmV5b3JEYXRhLkNvbnZleW9yX1J1bm5pbmdcIilcbiAgICAgICAgfSk7XG4gICAgICB9LFxuICAgICAgXCJzZXRcIjogZnVuY3Rpb24gKHZhcmlhbnQpIHtcbiAgICAgICAgZmxleFNlcnZlckludGVybmFscy5zYW5kYm94Rmxvd0NvbnRleHQuc2V0KFxuICAgICAgICAgIFwiY29udmV5b3JEYXRhLkNvbnZleW9yX1J1bm5pbmdcIixcbiAgICAgICAgICB2YXJpYW50LnZhbHVlXG4gICAgICAgICk7XG4gICAgICAgIHJldHVybiBvcGN1YS5TdGF0dXNDb2Rlcy5Hb29kO1xuICAgICAgfVxuICAgIH1cbiAgfSk7XG5cbiAgY29uc3QgTGluZTRfRmF1bHQgPSBuYW1lc3BhY2UuYWRkVmFyaWFibGUoe1xuICAgIFwib3JnYW5pemVkQnlcIjogY29udmV5b3JCb29scyxcbiAgICBcImJyb3dzZU5hbWVcIjogXCJMaW5lIDQgRmF1bHRlZFwiLFxuICAgIFwibm9kZUlkXCI6IFwibnM9MTtzPUxpbmU0X0ZhdWx0XCIsXG4gICAgXCJkYXRhVHlwZVwiOiBcIkJvb2xlYW5cIixcbiAgICBcInZhbHVlXCI6IHtcbiAgICAgIFwiZ2V0XCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBWYXJpYW50KHtcbiAgICAgICAgICBcImRhdGFUeXBlXCI6IERhdGFUeXBlLkJvb2xlYW4sXG4gICAgICAgICAgXCJ2YWx1ZVwiOiBmbGV4U2VydmVySW50ZXJuYWxzLnNhbmRib3hGbG93Q29udGV4dC5nZXQoXCJjb252ZXlvckRhdGEuTGluZTRfRmF1bHRcIilcbiAgICAgICAgfSk7XG4gICAgICB9LFxuICAgICAgXCJzZXRcIjogZnVuY3Rpb24gKHZhcmlhbnQpIHtcbiAgICAgICAgZmxleFNlcnZlckludGVybmFscy5zYW5kYm94Rmxvd0NvbnRleHQuc2V0KFxuICAgICAgICAgIFwiY29udmV5b3JEYXRhLkxpbmU0X0ZhdWx0XCIsXG4gICAgICAgICAgdmFyaWFudC52YWx1ZVxuICAgICAgICApO1xuICAgICAgICByZXR1cm4gb3BjdWEuU3RhdHVzQ29kZXMuR29vZDtcbiAgICAgIH1cbiAgICB9XG4gIH0pO1xuXG4gIGNvbnN0IExpbmU0X1N0YXRlID0gbmFtZXNwYWNlLmFkZFZhcmlhYmxlKHtcbiAgICBcIm9yZ2FuaXplZEJ5XCI6IGNvbnZleW9yRElOVHMsXG4gICAgXCJicm93c2VOYW1lXCI6IFwiTGluZSA0IFN0YXRlXCIsXG4gICAgXCJub2RlSWRcIjogXCJucz0xO3M9TGluZTRfU3RhdGVcIixcbiAgICBcImRhdGFUeXBlXCI6IFwiSW50MzJcIixcbiAgICBcInZhbHVlXCI6IHtcbiAgICAgIFwiZ2V0XCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBWYXJpYW50KHtcbiAgICAgICAgICBcImRhdGFUeXBlXCI6IERhdGFUeXBlLkludDMyLFxuICAgICAgICAgIFwidmFsdWVcIjogZmxleFNlcnZlckludGVybmFscy5zYW5kYm94Rmxvd0NvbnRleHQuZ2V0KFwiY29udmV5b3JEYXRhLkxpbmU0X1N0YXRlXCIpXG4gICAgICAgIH0pO1xuICAgICAgfSxcbiAgICAgIFwic2V0XCI6IGZ1bmN0aW9uICh2YXJpYW50KSB7XG4gICAgICAgIGZsZXhTZXJ2ZXJJbnRlcm5hbHMuc2FuZGJveEZsb3dDb250ZXh0LnNldChcbiAgICAgICAgICBcImNvbnZleW9yRGF0YS5MaW5lNF9TdGF0ZVwiLFxuICAgICAgICAgIHZhcmlhbnQudmFsdWVcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuIG9wY3VhLlN0YXR1c0NvZGVzLkdvb2Q7XG4gICAgICB9XG4gICAgfVxuICB9KTtcblxuICBjb25zdCBSb2JvdF9Qb3NpdGlvbiA9IG5hbWVzcGFjZS5hZGRWYXJpYWJsZSh7XG4gICAgXCJvcmdhbml6ZWRCeVwiOiBjb252ZXlvckZsb2F0cyxcbiAgICBcImJyb3dzZU5hbWVcIjogXCJSb2JvdCBBeGlzIEExIFBvc2l0aW9uXCIsXG4gICAgXCJub2RlSWRcIjogXCJucz0xO3M9Um9ib3RfUG9zaXRpb25cIixcbiAgICBcImRhdGFUeXBlXCI6IFwiRmxvYXRcIixcbiAgICBcInZhbHVlXCI6IHtcbiAgICAgIFwiZ2V0XCI6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBWYXJpYW50KHtcbiAgICAgICAgICBcImRhdGFUeXBlXCI6IERhdGFUeXBlLkZsb2F0LFxuICAgICAgICAgIFwidmFsdWVcIjogZmxleFNlcnZlckludGVybmFscy5zYW5kYm94Rmxvd0NvbnRleHQuZ2V0KFwiY29udmV5b3JEYXRhLlJvYm90X1Bvc2l0aW9uXCIpXG4gICAgICAgIH0pO1xuICAgICAgfSxcbiAgICAgIFwic2V0XCI6IGZ1bmN0aW9uICh2YXJpYW50KSB7XG4gICAgICAgIGZsZXhTZXJ2ZXJJbnRlcm5hbHMuc2FuZGJveEZsb3dDb250ZXh0LnNldChcbiAgICAgICAgICBcImNvbnZleW9yRGF0YS5Sb2JvdF9Qb3NpdGlvblwiLFxuICAgICAgICAgIHBhcnNlRmxvYXQodmFyaWFudC52YWx1ZSlcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuIG9wY3VhLlN0YXR1c0NvZGVzLkdvb2Q7XG4gICAgICB9XG4gICAgfVxuICB9KTtcblxuICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAvLyBBZGQgYSB2aWV3XG4gIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gIGNvbnN0IHZpZXdCb29scyA9IG5hbWVzcGFjZS5hZGRWaWV3KHtcbiAgICBcIm9yZ2FuaXplZEJ5XCI6IHJvb3RGb2xkZXIudmlld3MsXG4gICAgXCJicm93c2VOYW1lXCI6IFwiTGluZSA0IENvbnZleW9yIEJvb2xzXCJcbiAgfSk7XG5cbiAgY29uc3Qgdmlld0RJTlRzID0gbmFtZXNwYWNlLmFkZFZpZXcoe1xuICAgIFwib3JnYW5pemVkQnlcIjogcm9vdEZvbGRlci52aWV3cyxcbiAgICBcImJyb3dzZU5hbWVcIjogXCJMaW5lNCBDb252ZXlvciBESU5Uc1wiXG4gIH0pO1xuXG4gIGNvbnN0IHZpZXdGbG9hdHMgPSBuYW1lc3BhY2UuYWRkVmlldyh7XG4gICAgXCJvcmdhbml6ZWRCeVwiOiByb290Rm9sZGVyLnZpZXdzLFxuICAgIFwiYnJvd3NlTmFtZVwiOiBcIkxpbmU0IENvbnZleW9yIEZsb2F0c1wiXG4gIH0pO1xuXG4gIHZpZXdCb29scy5hZGRSZWZlcmVuY2Uoe1xuICAgIFwicmVmZXJlbmNlVHlwZVwiOiBcIk9yZ2FuaXplc1wiLFxuICAgIFwibm9kZUlkXCI6IENvbnZleW9yX1JUUy5ub2RlSWRcbiAgfSk7XG5cbiAgdmlld0Jvb2xzLmFkZFJlZmVyZW5jZSh7XG4gICAgXCJyZWZlcmVuY2VUeXBlXCI6IFwiT3JnYW5pemVzXCIsXG4gICAgXCJub2RlSWRcIjogUm9ib3RfUlRTLm5vZGVJZFxuICB9KTtcblxuICB2aWV3Qm9vbHMuYWRkUmVmZXJlbmNlKHtcbiAgICBcInJlZmVyZW5jZVR5cGVcIjogXCJPcmdhbml6ZXNcIixcbiAgICBcIm5vZGVJZFwiOiBDb252ZXlvcl9SdW5uaW5nLm5vZGVJZFxuICB9KTtcblxuICB2aWV3Qm9vbHMuYWRkUmVmZXJlbmNlKHtcbiAgICBcInJlZmVyZW5jZVR5cGVcIjogXCJPcmdhbml6ZXNcIixcbiAgICBcIm5vZGVJZFwiOiBMaW5lNF9GYXVsdC5ub2RlSWRcbiAgfSk7XG5cblxuICB2aWV3RElOVHMuYWRkUmVmZXJlbmNlKHtcbiAgICBcInJlZmVyZW5jZVR5cGVcIjogXCJPcmdhbml6ZXNcIixcbiAgICBcIm5vZGVJZFwiOiBMaW5lNF9TdGF0ZS5ub2RlSWRcbiAgfSk7XG5cbiAgdmlld0Zsb2F0cy5hZGRSZWZlcmVuY2Uoe1xuICAgIFwicmVmZXJlbmNlVHlwZVwiOiBcIk9yZ2FuaXplc1wiLFxuICAgIFwibm9kZUlkXCI6IFJvYm90X1Bvc2l0aW9uLm5vZGVJZFxuICB9KTtcbiAgY29yZVNlcnZlci5kZWJ1Z0xvZyhcImNyZWF0ZSBkeW5hbWljIGFkZHJlc3Mgc3BhY2UgZG9uZVwiKTtcbiAgbm9kZS53YXJuKFwiY29uc3RydWN0aW9uIG9mIG5ldyBhZGRyZXNzIHNwYWNlIGZvciBPUEMgVUEgZG9uZVwiKTtcblxuICBkb25lKCk7XG59XG4iLCJ4Ijo5NjAsInkiOjYwMCwid2lyZXMiOltdfSx7ImlkIjoiN2FlODlmMTM0NDE1YzUxZSIsInR5cGUiOiJldGgtaXAgaW4iLCJ6IjoiMmU4YzdmNWMuYWI3M2QiLCJlbmRwb2ludCI6ImYwMTIwNDJiNzUxNzNiNzciLCJtb2RlIjoiYWxsIiwidmFyaWFibGUiOiIiLCJwcm9ncmFtIjoiIiwibmFtZSI6IlJlYWQgTGluZTQgQ29udmV5b3IgdGFncyIsIngiOjE1MCwieSI6NjAwLCJ3aXJlcyI6W1siMGM1MWY0NGJhYTA4YTNiMiJdXX0seyJpZCI6ImEwNTkyMjgwYmFlYTk3NWIiLCJ0eXBlIjoiY29tbWVudCIsInoiOiIyZThjN2Y1Yy5hYjczZCIsIm5hbWUiOiJyZWFkIGRhdGEgZnJvbSBQTEMgJiBzdG9yZSBpbiBjb252ZXlvckRhdGEgY29udGV4dCBmbG93IG1lbW9yeSIsImluZm8iOiIiLCJ4IjozNTAsInkiOjU0MCwid2lyZXMiOltdfSx7ImlkIjoiYjQxMWE1Y2U0NzQ5ZmE2MSIsInR5cGUiOiJjb21tZW50IiwieiI6IjJlOGM3ZjVjLmFiNzNkIiwibmFtZSI6IlNlY3VyZSBPUEMgVUEgU2VydmVyIFB1Ymxpc2hpbmcgUExDIGNvbnZleW9yRGF0YSIsImluZm8iOiIiLCJ4Ijo5ODAsInkiOjU0MCwid2lyZXMiOltdfSx7ImlkIjoiMGM1MWY0NGJhYTA4YTNiMiIsInR5cGUiOiJjaGFuZ2UiLCJ6IjoiMmU4YzdmNWMuYWI3M2QiLCJuYW1lIjoic3RvcmUgUExDIERhdGEgaW4gZmxvdyBjb250ZXh0IG1lbW9yeSIsInJ1bGVzIjpbeyJ0Ijoic2V0IiwicCI6ImNvbnZleW9yRGF0YS5Db252ZXlvcl9SVFMiLCJwdCI6ImZsb3ciLCJ0byI6InBheWxvYWQuQ29udmV5b3JfUlRTIiwidG90IjoibXNnIn0seyJ0Ijoic2V0IiwicCI6ImNvbnZleW9yRGF0YS5Db252ZXlvcl9SdW5uaW5nIiwicHQiOiJmbG93IiwidG8iOiJwYXlsb2FkLkNvbnZleW9yX1J1bm5pbmciLCJ0b3QiOiJtc2cifSx7InQiOiJzZXQiLCJwIjoiY29udmV5b3JEYXRhLkxpbmU0X0ZhdWx0IiwicHQiOiJmbG93IiwidG8iOiJwYXlsb2FkLkxpbmU0X0ZhdWx0IiwidG90IjoibXNnIn0seyJ0Ijoic2V0IiwicCI6ImNvbnZleW9yRGF0YS5MaW5lNF9TdGF0ZSIsInB0IjoiZmxvdyIsInRvIjoicGF5bG9hZC5MaW5lNF9TdGF0ZSIsInRvdCI6Im1zZyJ9LHsidCI6InNldCIsInAiOiJjb252ZXlvckRhdGEuUm9ib3RfUG9zaXRpb24iLCJwdCI6ImZsb3ciLCJ0byI6InBheWxvYWQuUm9ib3RfUG9zaXRpb24iLCJ0b3QiOiJtc2cifSx7InQiOiJzZXQiLCJwIjoiY29udmV5b3JEYXRhLlJvYm90X1JUUyIsInB0IjoiZmxvdyIsInRvIjoicGF5bG9hZC5Sb2JvdF9SVFMiLCJ0b3QiOiJtc2cifV0sImFjdGlvbiI6IiIsInByb3BlcnR5IjoiIiwiZnJvbSI6IiIsInRvIjoiIiwicmVnIjpmYWxzZSwieCI6NTMwLCJ5Ijo2MDAsIndpcmVzIjpbW11dfSx7ImlkIjoiZjAxMjA0MmI3NTE3M2I3NyIsInR5cGUiOiJldGgtaXAgZW5kcG9pbnQiLCJhZGRyZXNzIjoiMTkyLjE2OC4wLjUiLCJzbG90IjoiMCIsImN5Y2xldGltZSI6IjEwMDAiLCJuYW1lIjoiTGluZSA0IFBMQyIsInZhcnRhYmxlIjp7IiI6eyJDb252ZXlvcl9SVFMiOnsidHlwZSI6IkJPT0wifSwiQ29udmV5b3JfUnVubmluZyI6eyJ0eXBlIjoiQk9PTCJ9LCJMaW5lNF9GYXVsdCI6eyJ0eXBlIjoiQk9PTCJ9LCJMaW5lNF9TdGF0ZSI6eyJ0eXBlIjoiRElOVCJ9LCJSb2JvdF9Qb3NpdGlvbiI6eyJ0eXBlIjoiUkVBTCJ9LCJSb2JvdF9SVFMiOnsidHlwZSI6IkJPT0wifX19fV0=",[3438,3439,3440],"style",{},"html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":12,"searchDepth":730,"depth":730,"links":3442},[3443,3444,3445,3446,3447,3448,3454,3455,3456],{"id":33,"depth":730,"text":34},{"id":40,"depth":730,"text":41},{"id":227,"depth":730,"text":228},{"id":295,"depth":730,"text":296},{"id":441,"depth":730,"text":442},{"id":576,"depth":730,"text":577,"children":3449},[3450,3451,3452,3453],{"id":683,"depth":750,"text":684},{"id":978,"depth":750,"text":979},{"id":1275,"depth":750,"text":1276},{"id":2595,"depth":750,"text":2596},{"id":3021,"depth":730,"text":3022},{"id":3081,"depth":730,"text":3082},{"id":3226,"depth":730,"text":3227},"Learn how to build Build and Deploy a custom OPC UA Server in Node-RED","md",{},"\u002Fnode-red\u002Fprotocol\u002Fopc-ua",{"title":5,"description":3457},"node-red\u002Fprotocol\u002Fopc-ua","TaU_SsDPNVyhaSLUyUPCCn1xJ9Ka3_WwPn0v3JgIi_4",1780070557906]