Пример #1
0
        public async Task <MsgOPCUAMethodCallResponse> CallMethodAsync(string methodId, Dictionary <string, object> parameters, bool returnRawJSON = false)
        {
            MethodRegistration methodRegistration;

            if (!_MethodRegistrations.TryGetValue(methodId, out methodRegistration))
            {
                return(null);
            }
            if (methodRegistration.MethodThingAddress == null)
            {
                await UpdateRegistrationsAsync(false);
            }
            if (methodRegistration.MethodThingAddress == null)
            {
                return(null);
            }

            var methodCall = new MsgOPCUAMethodCall {
                Arguments = parameters, ReturnRawJSON = returnRawJSON
            };
            MsgOPCUAMethodCallResponse response;

            if (methodRegistration.MethodInfo.CallTimeout > 0)
            {
                response = await TheCommRequestResponse.PublishRequestJSonAsync <MsgOPCUAMethodCall, MsgOPCUAMethodCallResponse>(OwnerAddress, methodRegistration.MethodThingAddress,
                                                                                                                                 methodCall, new TimeSpan(0, 0, 0, 0, methodRegistration.MethodInfo.CallTimeout + 15));
            }
            else
            {
                response = await TheCommRequestResponse.PublishRequestJSonAsync <MsgOPCUAMethodCall, MsgOPCUAMethodCallResponse>(OwnerAddress, methodRegistration.MethodThingAddress,
                                                                                                                                 methodCall);
            }
            return(response);
        }
Пример #2
0
        public async Task <string> DisconnectAsync()
        {
            var response = await TheCommRequestResponse.PublishRequestJSonAsync <MsgOPCUADisconnect, MsgOPCUADisconnectResponse>(OwnerAddress, OpcThingAddress, new MsgOPCUADisconnect());

            if (response == null)
            {
                return("Error sending disconnect message");
            }
            return(response.Error);
        }
Пример #3
0
        public async Task <MsgOPCUAReadTagsResponse> ReadTagsAsync(List <string> nodeIds, TimeSpan callTimeout)
        {
            var response = await TheCommRequestResponse.PublishRequestJSonAsync <MsgOPCUAReadTags, MsgOPCUAReadTagsResponse>(OwnerAddress, OpcThingAddress, new MsgOPCUAReadTags
            {
                Tags = nodeIds.Select((n) => new MsgOPCUAReadTags.TagInfo {
                    NodeId = n
                }).ToList()
            }, callTimeout);

            return(response);
        }
Пример #4
0
        public Task <MsgUnsubscribeSensorsResponse> UnsubscribeSensorsAsync(MsgUnsubscribeSensors unsubscribeRequest, bool bypassCapabilityCheck)
        {
            if (!bypassCapabilityCheck && !this.Capabilities.Contains(eThingCaps.SensorProvider))
            {
                return(TheCommonUtils.TaskFromResult(new MsgUnsubscribeSensorsResponse {
                    Error = "Thing is not a sensor provider"
                }));
            }
            ;
            var unsubscribeResponseTask = TheCommRequestResponse.PublishRequestJSonAsync <MsgUnsubscribeSensors, MsgUnsubscribeSensorsResponse>(this, unsubscribeRequest);

            return(unsubscribeResponseTask);
        }
Пример #5
0
        public Task <MsgGetSensorSubscriptionsResponse> GetSensorProviderSubscriptionsAsync(MsgGetSensorSubscriptions getSubscriptionsRequest, bool bypassCapabilityCheck)
        {
            if (!bypassCapabilityCheck && !this.Capabilities.Contains(eThingCaps.SensorProvider))
            {
                return(TheCommonUtils.TaskFromResult(new MsgGetSensorSubscriptionsResponse {
                    Error = "Thing is not a sensor provider"
                }));
            }
            ;
            var getSubscriptionResponseTask = TheCommRequestResponse.PublishRequestJSonAsync <MsgGetSensorSubscriptions, MsgGetSensorSubscriptionsResponse>(this, getSubscriptionsRequest);

            return(getSubscriptionResponseTask);
        }
Пример #6
0
        public Task <MsgBrowseSensorsResponse> ProviderBrowseSensorsAsync(TheThing.MsgBrowseSensors browseRequest, bool bypassCapabilityCheck)
        {
            if (!bypassCapabilityCheck && !this.Capabilities.Contains(eThingCaps.SensorProvider))
            {
                return(TheCommonUtils.TaskFromResult(new MsgBrowseSensorsResponse {
                    Error = "Thing is not a sensor provider"
                }));
            }
            ;
            var browseResponseTask = TheCommRequestResponse.PublishRequestJSonAsync <TheThing.MsgBrowseSensors, TheThing.MsgBrowseSensorsResponse>(this, browseRequest);

            return(browseResponseTask);
        }
Пример #7
0
        public Task <MsgSubscribeSensorsResponse <subscriptionT> > SubscribeSensorsAsync <subscriptionT>(MsgSubscribeSensors <subscriptionT> subscribeRequest, bool bypassCapabilityCheck) where subscriptionT : TheSensorSubscription
        {
            if (!bypassCapabilityCheck && !this.Capabilities.Contains(eThingCaps.SensorProvider))
            {
                return(TheCommonUtils.TaskFromResult(new MsgSubscribeSensorsResponse <subscriptionT> {
                    Error = "Thing is not a sensor provider"
                }));
            }
            ;
            var subscribeResponseTask = TheCommRequestResponse.PublishRequestJSonAsync <TheThing.MsgSubscribeSensors <subscriptionT>, TheThing.MsgSubscribeSensorsResponse <subscriptionT> >(this, subscribeRequest);

            return(subscribeResponseTask);
        }
Пример #8
0
        public static TheRecordHolder MeshQueryRecordHolder(int idRecord, Guid node, string strEngineName, Guid cdeMIdThing)
        {
            TheRecordHolder trh = null;

            // Package up request info.
            MsgMileRecordHolder msgRequest = new MsgMileRecordHolder()
            {
                id = idRecord
            };

            // Start asynchronous task to send a message and wait for a reply.
            // Sends a message named nameof(MsgMileRecordHolder)
            // Receives a reply named nameof(MsgMileRecordHolderResponse)
            // See function "HandleMessage" for actual handling.

            Task <MsgMileRecordHolderResponse> t = null;

            try
            {
                TheMessageAddress tma = new TheMessageAddress()
                {
                    Node       = Guid.Empty,
                    EngineName = strEngineName,
                    ThingMID   = cdeMIdThing,
                    SendToProvisioningService = false,
                };
                t = TheCommRequestResponse.PublishRequestJSonAsync <MsgMileRecordHolder, MsgMileRecordHolderResponse>(tma, msgRequest);
            }
            catch (Exception ex)
            {
                string strMessage = ex.Message;
            }

            // Wait for a bit
            t.Wait(20000);
            bool bTaskCompleted = t.IsCompleted;

            // Check for success.
            if (bTaskCompleted)
            {
                MsgMileRecordHolderResponse msgResponse = t.Result;
                trh = msgResponse.data;
            }

            return(trh);
        }
Пример #9
0
        public async Task <string> ConnectAsync(OPCUAConnectParameters connectParams = null, bool logEssentialOnly = false)
        {
            if (connectParams != null)
            {
                ConnectParameters = new OPCUAConnectParameters(connectParams);
            }

            if (ConnectParameters != null)
            {
                // TODO make this work for remote things as well
                var tThing = GetOpcThing();
                TheThing.SetSafePropertyString(tThing, "UserName", ConnectParameters.UserName);
                TheThing.SetSafePropertyString(tThing, "Password", ConnectParameters.Password);
            }

            var response = await TheCommRequestResponse.PublishRequestJSonAsync <MsgOPCUAConnect, MsgOPCUAConnectResponse>(OwnerAddress, OpcThingAddress, new MsgOPCUAConnect { LogEssentialOnly = logEssentialOnly });

            if (response == null)
            {
                return("Error sending connect message");
            }
            return(response.Error);
        }
Пример #10
0
        private void SendButton_Click(object sender, RoutedEventArgs e)
        {
            var msgHello = new MsgChatHello
            {
                Message    = MessageText.Text,
                SenderName = UserName.Text,
            };

            var target = new TheMessageAddress
            {
                EngineName = strChatEngine, // Send to this engine
                Node       = Guid.Empty     // Send to all nodes in the mesh
            };

            var response = TheCommRequestResponse.PublishRequestJSonAsync <MsgChatHello, MsgChatHelloResponse>(
                TheThingRegistry.GetBaseEngineAsThing(eEngineName.ThingService), // Since we have no plug-in, use an arbitrary existing thing in the system as the originator
                target,
                msgHello,
                new TimeSpan(0, 0, 10)) // Wait 10 seconds for ackknowledge message
                           .Result;

            if (response == null)
            {
                MessageBox.Show("Error sending message or no acknowlege received.");
            }
            else
            {
                if (response.Acknowledged)
                {
                }
                else
                {
                    MessageBox.Show("Message not acknowledged.");
                }
            }
        }
Пример #11
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="reRegisterAll"></param>
        /// <returns></returns>
        public async Task <List <RegistrationError> > UpdateRegistrationsAsync(bool reRegisterAll = false, bool bulkApply = false)
        {
            var tags    = new List <TagRegistration>();
            var methods = new List <MethodRegistration>();
            var events  = new List <EventRegistration>();

            foreach (var tagRegistration in _TagRegistrations.Values)
            {
                if (reRegisterAll || !String.IsNullOrEmpty(tagRegistration.Error))
                {
                    tagRegistration.Error = "pending";
                    tags.Add(tagRegistration);
                }
            }
            foreach (var methodRegistration in _MethodRegistrations.Values)
            {
                if (reRegisterAll || !String.IsNullOrEmpty(methodRegistration.Error))
                {
                    methodRegistration.Error = "pending";
                    methods.Add(methodRegistration);
                }
            }

            foreach (var eventRegistration in _EventRegistrations.Values)
            {
                if (reRegisterAll || !String.IsNullOrEmpty(eventRegistration.Error))
                {
                    eventRegistration.Error = "pending";
                    events.Add(eventRegistration);
                }
            }

            if (tags.Count == 0 && methods.Count == 0 && events.Count == 0)
            {
                return(new List <RegistrationError>());
            }

            var response = await TheCommRequestResponse.PublishRequestJSonAsync <MsgOPCUACreateTags, MsgOPCUACreateTagsResponse>(OwnerAddress, OpcThingAddress, new MsgOPCUACreateTags
            {
                Tags      = tags.Count > 0 ? tags.Select((t) => t.TagInfo).ToList() : null,
                Methods   = methods.Count > 0 ? methods.Select((m) => m.MethodInfo).ToList() : null,
                Events    = events.Count > 0 ? events.Select(e => e.EventInfo).ToList() : null,
                BulkApply = bulkApply
            });

            if (response == null)
            {
                return(null);
            }
            if (response.Results == null)
            {
                return(new List <RegistrationError> {
                    new RegistrationError {
                        Error = response.Error,
                    }
                });
            }

            if (response.Results.Count != tags.Count + methods.Count + events.Count)
            {
                return(new List <RegistrationError> {
                    new RegistrationError {
                        Error = String.Format("Internal error (result count mismatch: {0}, expected {1}", response.Results.Count, tags.Count + methods.Count),
                    }
                });
            }

            var registrationErrors = new List <RegistrationError>();

            int resultIndex = 0;
            var results     = response.Results;

            foreach (var tag in tags)
            {
                var error = results[resultIndex].Error;
                if (String.IsNullOrEmpty(error))
                {
                    tag.Error = null;
                }
                else
                {
                    tag.Error = error;
                    registrationErrors.Add(new RegistrationError
                    {
                        PropertyName = tag.PropertyName,
                        NodeId       = tag.TagInfo.NodeId,
                        Error        = error,
                    });
                }
                resultIndex++;
            }
            foreach (var method in methods)
            {
                var error = results[resultIndex].Error;
                if (String.IsNullOrEmpty(error))
                {
                    method.Error = null;
                    method.MethodThingAddress = results[resultIndex].MethodThingAddress;
                }
                else
                {
                    method.Error = error;
                    registrationErrors.Add(new RegistrationError
                    {
                        MethodId = method.MethodId,
                        Error    = error,
                    });
                }
                resultIndex++;
            }
            foreach (var eventRegistration in events)
            {
                var error = results[resultIndex].Error;
                if (String.IsNullOrEmpty(error))
                {
                    eventRegistration.Error = null;
                }
                else
                {
                    eventRegistration.Error = error;
                    registrationErrors.Add(new RegistrationError
                    {
                        PropertyName = eventRegistration.PropertyName,
                        NodeId       = eventRegistration.EventInfo.NodeId,
                        Error        = error,
                    });
                }
                resultIndex++;
            }

            return(registrationErrors);
        }
Пример #12
0
        static protected TheThing StartOPCServer(bool disableSecurity)
        {
#if OPCUASERVER
            // TODO Actually use our own OPC Server for the unit test
            var opcServerThing = TheThingRegistry.GetThingsOfEngine("CDMyOPCUAServer.cdeMyOPCServerService").FirstOrDefault();
            Assert.IsNotNull(opcServerThing, $"Unable to obtain OPC Server thing: error loading plug-in or server not yet initialized?");

            lock (opcServerStartupLock)
            {
                if (!TheThing.GetSafePropertyBool(opcServerThing, "IsRunning"))
                {
                    TheThing.SetSafePropertyBool(opcServerThing, "DisableSecurity", disableSecurity);
                    TheThing.SetSafePropertyBool(opcServerThing, "NoServerCertificate", disableSecurity);

                    var theOpcThing = new TheThing();
                    theOpcThing.EngineName = "OPCTestEng";
                    theOpcThing.DeviceType = "OPCTestDT";
                    theOpcThing.Address    = "OPCTestAddress";
                    theOpcThing.SetProperty("OpcProp01", "0001");
                    theOpcThing.SetProperty("OpcProp02", "0002");
                    theOpcThing.SetProperty("OpcProp03", "0003");
                    theOpcThing.SetProperty("OpcProp04", "0004");
                    theOpcThing.SetProperty("OpcProp05", "0005");
                    theOpcThing.SetProperty("OpcProp06", "0006");
                    theOpcThing.SetProperty("OpcProp07", "0007");
                    theOpcThing.SetProperty("OpcProp08", "0008");
                    theOpcThing.SetProperty("OpcProp09", "0009");
                    theOpcThing.SetProperty("OpcProp10", "0010");
                    var tThing = TheThingRegistry.RegisterThing(theOpcThing);
                    Assert.IsNotNull(tThing);

                    var addThingResponse = TheCommRequestResponse.PublishRequestJSonAsync <MsgAddThingsToServer, MsgAddThingsToServerResponse>(myContentService, opcServerThing, new MsgAddThingsToServer
                                                                                                                                               (
                                                                                                                                                   new TheThingToAddToServer
                    {
                        cdeMID = Guid.NewGuid(),
                        ReplaceExistingThing = false,
                        ThingMID             = TheCommonUtils.cdeGuidToString(theOpcThing.cdeMID),
                    }
                                                                                                                                               ), new TimeSpan(0, 0, 30)).Result;

                    Assert.IsNotNull(addThingResponse, "No reply to OPC Server MsgAddThingToServer");
                    Assert.IsTrue(string.IsNullOrEmpty(addThingResponse.Error), $"Error adding thing to OPC Server: '{addThingResponse.Error}'.");
                    Assert.AreEqual(1, addThingResponse.ThingStatus.Count, $"Error adding thing to OPC Server.");
                    Assert.IsTrue(string.IsNullOrEmpty(addThingResponse.ThingStatus[0].Error), $"Error adding thing to OPC Server: '{addThingResponse.ThingStatus[0].Error}'.");

                    MsgStartStopServerResponse responseStart;
                    int retryCount = 1;
                    do
                    {
                        responseStart = TheCommRequestResponse.PublishRequestJSonAsync <MsgStartStopServer, MsgStartStopServerResponse>(myContentService, opcServerThing,
                                                                                                                                        new MsgStartStopServer
                        {
                            Restart = true,
                        },
                                                                                                                                        new TimeSpan(0, 0, 30)).Result;
                        retryCount--;
                    } while (retryCount >= 0 && responseStart == null);

                    Assert.IsNotNull(responseStart, "Failed to send MsgStartStopServer message to restart OPC UA Server");
                    Assert.IsTrue(string.IsNullOrEmpty(responseStart.Error), $"Error restarting OPC Server: '{addThingResponse.Error}'.");
                    Assert.IsTrue(responseStart.Running, $"OPC Server not running after MsgStartStopServer Restart message");
                }
            }
            return(opcServerThing);
#else
            return(null);
#endif
        }
Пример #13
0
 public Task <MsgUnsubscribeFromThingsResponse> UnsubscribeFromThingsAsync(MsgUnsubscribeFromThings unsubscribeThingsRequest)
 {
     return(TheCommRequestResponse.PublishRequestJSonAsync <MsgUnsubscribeFromThings, MsgUnsubscribeFromThingsResponse>(this, unsubscribeThingsRequest));
 }
Пример #14
0
 public Task <MsgGetThingSubscriptionsResponse> GetThingSubscriptionsAsync(MsgGetThingSubscriptions getSubscriptionsRequest)
 {
     return(TheCommRequestResponse.PublishRequestJSonAsync <MsgGetThingSubscriptions, MsgGetThingSubscriptionsResponse>(this, getSubscriptionsRequest));
 }
Пример #15
0
 public Task <MsgSubscribeToThingsResponse> SubscribeToThingsAsync(MsgSubscribeToThings subscribeThingsRequest)
 {
     return(TheCommRequestResponse.PublishRequestJSonAsync <MsgSubscribeToThings, MsgSubscribeToThingsResponse>(this, subscribeThingsRequest));
 }
Пример #16
0
        public void CreateOPCWizard()
        {
            TheFieldInfo tTargetButton = null;
            var          flds          = TheNMIEngine.AddNewWizard <TheOPCSetClass>(new Guid("{56565656-6AD1-45AE-BE61-96AF02329614}"), Guid.Empty, TheNMIEngine.GetEngineDashBoardByThing(MyBaseThing).cdeMID, "Welcome to the OPC Wizard",
                                                                                    new nmiCtrlWizard {
                PanelTitle = "<i class='fa faIcon fa-3x'>&#xf0d0;</i></br>New OPC Client", SideBarTitle = "New OPC Client Wizard", SideBarIconFA = "&#xf545;", TileThumbnail = "FA5:f545"
            },
                                                                                    (myClass, pClientInfo) =>
            {
                myClass.cdeMID   = Guid.Empty;
                TheThing tMemTag = null;
                if (myClass.CreateMemoryTag)
                {
                    var tReq = new TheThingRegistry.MsgCreateThingRequestV1()
                    {
                        EngineName       = "CDMyVThings.TheVThings",
                        DeviceType       = "Memory Tag",
                        FriendlyName     = myClass.ClientName,
                        OwnerAddress     = MyBaseThing,
                        InstanceId       = Guid.NewGuid().ToString(),
                        CreateIfNotExist = true
                    };
                    tMemTag = TheThingRegistry.CreateOwnedThingAsync(tReq).Result;
                }

                var tOPCReq = new TheThingRegistry.MsgCreateThingRequestV1()
                {
                    EngineName       = "CDMyOPCUAClient.cdeOPCUaClient",
                    DeviceType       = "OPC-UA Remote Server",
                    FriendlyName     = myClass.ClientName,
                    Address          = myClass.OPCAddress,
                    OwnerAddress     = MyBaseThing,
                    InstanceId       = Guid.NewGuid().ToString(),
                    CreateIfNotExist = true
                };
                tOPCReq.Properties = new Dictionary <string, object>();
                //tOPCReq.Properties["ID"] = Guid.NewGuid().ToString();
                tOPCReq.Properties["AutoConnect"]     = myClass.AutoConnect;
                tOPCReq.Properties["SendOpcDataType"] = true;
                if (!myClass.DisableSecurity)
                {
                    tOPCReq.Properties["DisableSecurity"]            = true;
                    tOPCReq.Properties["AcceptUntrustedCertificate"] = true;
                    tOPCReq.Properties["DisableDomainCheck"]         = true;
                    tOPCReq.Properties["AcceptInvalidCertificate"]   = true;
                    tOPCReq.Properties["Anonymous"] = true;
                }
                if (tMemTag != null)
                {
                    tOPCReq.Properties["TagHostThingForSubscribeAll"] = tMemTag.cdeMID;
                }
                var tOPCServer = TheThingRegistry.CreateOwnedThingAsync(tOPCReq).Result;
                try
                {
                    if (tOPCServer != null && myClass.Prop2Tag && myClass.AutoConnect)
                    {
                        var response = TheCommRequestResponse.PublishRequestJSonAsync <MsgOPCUAConnect, MsgOPCUAConnectResponse>(MyBaseThing, tOPCServer, new MsgOPCUAConnect {
                            LogEssentialOnly = true, WaitUntilConnected = true
                        }).Result;
                        if (response != null && string.IsNullOrEmpty(response.Error))
                        {
                            var tBrowseResponse = TheCommRequestResponse.PublishRequestJSonAsync <MsgOPCUABrowse, MsgOPCUABrowseResponse>(MyBaseThing, tOPCServer, new MsgOPCUABrowse()).Result;
                            if (string.IsNullOrEmpty(tBrowseResponse.Error))
                            {
                                TheCommCore.PublishToNode(pClientInfo.NodeID, new TSM(eEngineName.NMIService, "NMI_TOAST", $"OPC UA Client browse error: {tBrowseResponse.Error}"));
                                return;
                            }
                            else
                            {
                                List <MsgOPCUACreateTags.TagInfo> tTagList = tBrowseResponse.Tags;
                                if (tTagList != null && tTagList.Count > 0)
                                {
                                    var tres = TheCommRequestResponse.PublishRequestJSonAsync <MsgOPCUACreateTags, MsgOPCUACreateTagsResponse>(MyBaseThing, tOPCServer, new MsgOPCUACreateTags {
                                        Tags = tTagList, BulkApply = true
                                    }).Result;
                                    if (tres != null && string.IsNullOrEmpty(tres.Error))
                                    {
                                        TheCommCore.PublishToNode(pClientInfo.NodeID, new TSM(eEngineName.NMIService, "NMI_TOAST", "OPC UA Client Created and memory tag ready"));
                                    }
                                }
                            }
                        }
                    }
                    TheCommCore.PublishToNode(pClientInfo.NodeID, new TSM(eEngineName.NMIService, "NMI_TOAST", "OPC UA Client Created and ready"));
                }
                catch (Exception)
                {
                    TheCommCore.PublishToNode(pClientInfo.NodeID, new TSM(eEngineName.NMIService, "NMI_TOAST", "Something went wrong! Check the OPC and Memory Tag settings"));
                }
                tTargetButton.SetUXProperty(pClientInfo.NodeID, $"OnClick=TTS:{tOPCServer.GetBaseThing().cdeMID}");
                TheCommonUtils.SleepOneEye(2000, 100);
            });

            var tMyForm2 = flds["Form"] as TheFormInfo;

            var tFlds = TheNMIEngine.AddNewWizardPage(MyBaseThing, tMyForm2, 0, 1, 2, null /*"Name and Address"*/);

            TheNMIEngine.AddWizardControl(MyBaseThing, tMyForm2, eFieldType.SingleEnded, 1, 1, 2, 0, "Connection Name", "ClientName", new TheNMIBaseControl {
                Explainer = "1. Enter name for the new OPC connection.",
            });
            TheNMIEngine.AddWizardControl(MyBaseThing, tMyForm2, eFieldType.SingleEnded, 1, 2, 2, 0, "OPC Server Address", "OPCAddress", new TheNMIBaseControl {
                Explainer = "1. Enter address of the OPC server.",
            });

            tFlds = TheNMIEngine.AddNewWizardPage(MyBaseThing, tMyForm2, 1, 2, 3, null /* "Settings"*/);
            TheNMIEngine.AddWizardControl(MyBaseThing, tMyForm2, eFieldType.SingleCheck, 2, 1, 2, 0, "OPC Server requires Security", "DisableSecurity", new nmiCtrlSingleCheck {
                Explainer = "Check if the OPC Server requires security"
            });
            TheNMIEngine.AddWizardControl(MyBaseThing, tMyForm2, eFieldType.SingleCheck, 2, 2, 2, 0, "Create a Memory Tag", "CreateMemoryTag", new nmiCtrlSingleCheck {
                TileWidth = 3, Explainer = "Check to create a Memory Tag and check to subscribes all Tags into it"
            });
            TheNMIEngine.AddWizardControl(MyBaseThing, tMyForm2, eFieldType.SingleCheck, 2, 3, 2, 0, "All Tags in Memory Tag", "Prop2Tag", new nmiCtrlSingleCheck {
                TileWidth = 3
            });
            TheNMIEngine.AddWizardControl(MyBaseThing, tMyForm2, eFieldType.SingleCheck, 2, 4, 2, 0, "Auto Connect to Server", "AutoConnect", new nmiCtrlSingleCheck {
                TileWidth = 3, Explainer = "Don't select this if your server requires security settings"
            });

            tFlds = TheNMIEngine.AddNewWizardPage(MyBaseThing, tMyForm2, 2, 3, 0, null /*"Final Settings"*/);
            TheNMIEngine.AddWizardControl(MyBaseThing, tMyForm2, eFieldType.SmartLabel, 3, 2, 0, 0, null, null, new nmiCtrlSmartLabel {
                Text = "Once you click finish, the Wizard will create the items you requested. It will notify you with a toast when its done", TileHeight = 5, TileWidth = 7, NoTE = true
            });
            //HELP SECTION final step help section

            TheNMIEngine.AddWizardProcessPage(MyBaseThing, tMyForm2, 4);
            TheNMIEngine.AddWizardControl(MyBaseThing, tMyForm2, eFieldType.SmartLabel, 4, 1, 0, 0, null, null, new nmiCtrlSmartLabel {
                NoTE = true, TileWidth = 7, Text = "Creating the new instance..please wait", TileHeight = 2
            });

            TheNMIEngine.AddWizardFinishPage(MyBaseThing, tMyForm2, 5);
            TheNMIEngine.AddWizardControl(MyBaseThing, tMyForm2, eFieldType.SmartLabel, 5, 1, 0, 0, null, null, new nmiCtrlSmartLabel {
                NoTE = true, TileWidth = 7, Text = "Done...what do you want to do next?", TileHeight = 2
            });

            TheNMIEngine.AddWizardControl(MyBaseThing, tMyForm2, eFieldType.TileGroup, 5, 2, 0, 0, null, null, new nmiCtrlTileGroup {
                TileWidth = 1, TileHeight = 2, TileFactorX = 2
            });
            TheNMIEngine.AddWizardControl(MyBaseThing, tMyForm2, eFieldType.TileButton, 5, 3, 2, 0, "Go to Dashboard", null, new nmiCtrlTileButton {
                NoTE = true, TileHeight = 2, TileWidth = 3, OnClick = $"TTS:{mMyDashboard.cdeMID}", ClassName = "cdeTransitButton"
            });
            TheNMIEngine.AddWizardControl(MyBaseThing, tMyForm2, eFieldType.TileGroup, 5, 4, 0, 0, null, null, new nmiCtrlTileGroup {
                TileWidth = 1, TileHeight = 2
            });
            tTargetButton = TheNMIEngine.AddWizardControl(MyBaseThing, tMyForm2, eFieldType.TileButton, 5, 5, 2, 0, "Go to New OPC Client", null, new nmiCtrlTileButton {
                NoTE = true, TileHeight = 2, TileWidth = 3, ClassName = "cdeTransitButton"
            });
        }