diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5d6a5cd --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +################################################################################ +# This .gitignore file was automatically created by Microsoft(R) Visual Studio. +################################################################################ + +/.vs +/prjOnRampAPI/.vs/prjOnRampAPI +/prjOnRampAPI/prjOnRampAPI/obj +/prjOnRampAPI/obj +/prjOnRampAPI/bin/Debug/net8.0 +/prjOnRampAPI/.vs/ProjectEvaluation diff --git a/README b/README deleted file mode 100644 index efd5dea..0000000 --- a/README +++ /dev/null @@ -1,208 +0,0 @@ -# OnRamp SDK - -The **OnRamp SDK** provides supported libraries for integrating external applications with the OnRamp platform. - -Current capabilities include: - -* Accessing the **OnRamp API** -* Receiving **real-time Shop Monitor events** via WebSockets -* Running **registered OnRamp API queries** - -The SDK is distributed via **NuGet** to allow deterministic versioning and updateable integrations. - -This approach enables applications to interact with OnRamp **without requiring direct database access**, which is particularly important for hosted or cloud environments. - ---- - -# Installation - -Add the OnRamp SDK NuGet repository: - -```bash -dotnet nuget add source https://git.onramp-solutions.com/api/packages/OnRampSDK/nuget/index.json --name onrampsdk -``` - -After adding the source, packages will be available under: - -``` -Package Source: onrampsdk -``` - -Packages can then be installed using standard NuGet workflows in Visual Studio or via the .NET CLI. - ---- - -# Overview - -Applications integrating with OnRamp typically perform the following steps: - -1. Connect to the **OnRamp API** -2. Connect to the **OnRamp WebSocket service** -3. Register a **Socket ID** -4. Receive **Shop Monitor events** -5. Execute **API queries** to retrieve or process related data - -Incoming WebSocket messages function similarly to **interrupt events**. They are delivered with minimal latency when Shop Monitor activity occurs. - -Example event types include: - -* Work Order Login -* Work Order Logout -* Cycle Start -* Cycle Complete -* Device button events - -Applications can respond to these events by retrieving additional information through the OnRamp API. - ---- - -# Example Architecture - -A typical implementation: - -* Launches an application (console app, service, or background process) -* Connects to the OnRamp API -* Establishes a WebSocket connection -* Registers a **Socket ID** -* Waits indefinitely for incoming Shop Monitor events - -The application remains active and processes events until it is terminated. - -Multiple deployment models are possible: - -* A **single application handling all devices** -* Applications **per device** -* Applications **per event type** - ---- - -# Shop Monitor Integration - -Shop Monitor devices trigger events through a configured **Socket ID**. - -To configure a device: - -1. Open **S1280 – Device Shop Monitor** -2. Set the **Process ID / Socket ID** to the value expected by the application -3. The application registers with this same Socket ID - -Any application listening with that Socket ID will receive the event messages. - ---- - -# API Queries - -API queries must be registered in: - -``` -S5011 – API Queries -``` - -Supported query types: - -* `SELECT` -* `UPDATE` - -In most integrations, **SELECT queries are sufficient**. UPDATE queries should be used cautiously. - ---- - -# Query Parameters - -Queries use **Handlebars-style parameters**. - -Example: - -```sql -SELECT woo_oper, woo_qty_rep -FROM wo_oper -WHERE woo_nbr = '{{p:wonum}}' -``` - -`p:wonum` is a parameter passed by the client application. - ---- - -# Example Query Execution - -Example code for executing a query through the API: - -```vb -' Get all operations for a work order -Dim allOpsArgs As New Dictionary(Of String, String) -allOpsArgs.Add("wonum", woNum) - -' QL-10000006 -Dim rstDataAllOps = api.runQuery("QL-10000006", allOpsArgs) - -If rstDataAllOps.RecordCount > 0 Then - Do Until rstDataAllOps.EOF - Console.WriteLine(rstDataAllOps("woo_oper") + " - QtyRep:" & rstDataAllOps("woo_qty_rep")) - rstDataAllOps.MoveNext() - Loop -End If -``` - -The returned result set exposes a familiar **recordset-style interface**, allowing iteration using: - -* `RecordCount` -* `EOF` -* `MoveNext()` -* Column access via field name - ---- - -# Query Performance Recommendations - -For optimal performance: - -* Avoid returning very large datasets -* Do not use `SELECT *` -* Specify explicit column names -* Limit queries to only the data required by the application - -Example (recommended): - -```sql -SELECT pt_part, pt_desc -FROM pt_mstr -``` - -Example (not recommended): - -```sql -SELECT * -FROM pt_mstr -``` - ---- - -# Use Cases - -Common uses for the SDK include: - -* Shop Monitor automation -* Production event processing -* Device integrations -* External analytics services -* Integration with external systems -* Cloud-hosted integrations where database access is unavailable - ---- - -# Future Expansion - -The SDK is designed to support additional OnRamp platform integration capabilities over time, including: - -* Additional event channels -* Extended API functionality -* Additional integration services - ---- - -If you'd like, I can also help you add two sections that **good SDK repos almost always include but people forget**: - -* **Minimal working example (full program)** -* **Event message structure / enum definitions** - -Those make the repo dramatically easier for developers to adopt. diff --git a/README.md b/README.md new file mode 100644 index 0000000..95e6a6d --- /dev/null +++ b/README.md @@ -0,0 +1,247 @@ +# OnRamp SDK + +The **OnRamp SDK** provides supported libraries for integrating external applications with the **OnRamp platform**. + +The SDK allows applications to interact with OnRamp **without requiring direct database access**, enabling integrations for: + +- hosted environments +- cloud deployments +- external services +- device integrations + +The SDK is distributed via **NuGet** to provide deterministic versioning and controlled upgrades. + +--- + +# Features + +Current capabilities include: + +- Access to the **OnRamp API** +- Real-time **Shop Monitor events** via WebSockets +- Execution of **registered API queries** +- Event-driven application integrations + +Typical integration scenarios include: + +- Shop Monitor automation +- Production event processing +- Device integrations +- External analytics +- Cloud-hosted services + +--- + +# Installation + +Add the OnRamp SDK NuGet feed: + +```bash +dotnet nuget add source https://git.onramp-solutions.com/api/packages/OnRampSDK/nuget/index.json --name onrampsdk +``` + +After adding the source, packages will appear under: + +``` +Package Source: onrampsdk +``` + +Packages can then be installed using standard NuGet workflows. + +Example: + +```bash +dotnet add package ORPublicApiRefCore +dotnet add package ORSocketsCore +``` + +--- + +# Quick Start Example + +The following console example demonstrates a basic integration pattern: + +1. Connect to the OnRamp API +2. Establish a WebSocket connection +3. Register a Socket ID +4. Receive Shop Monitor events +5. Execute API queries + +```vb +Imports System.Threading +Imports ORPublicApiRefCore +Imports ORSocketsCore + +Module Program + + Private api As OnRampAPI + Private socketClient As ORSocketClient + Private quitEvent As ManualResetEvent = New ManualResetEvent(False) + + Sub Main(args As String()) + + ' OnRamp root endpoint + Dim envUrl As String = "https://onramp.site.com/PROD" + + ' Register for Shop Monitor events + socketClient = New ORSocketClient(envUrl, "SHOPMONITORCLIENT", "SITE1001") + + AddHandler socketClient.OnMessageReceived, AddressOf SocketMessageRec + AddHandler socketClient.OnLog, AddressOf SocketLog + + api = New OnRampAPI(envUrl) + + ' API authentication + api.APILogin("api_user", "api_password") + + WaitForConsoleExit() + + End Sub +``` + +--- + +# Shop Monitor Events + +Shop Monitor devices send events through a configured **Socket ID**. + +To configure a device: + +1. Open **S1280 Device Shop Monitor** +2. Set the **Process ID / Socket ID** +3. Use the same ID in the application + +Example event types include: + +- Work Order Login +- Work Order Logout +- Cycle Start +- Cycle Complete +- Device button events + +Incoming WebSocket messages behave similarly to **interrupt events**, allowing applications to respond immediately to shop floor activity. + +--- + +# API Queries + +Queries must be registered in: + +``` +S5011 API Queries +``` + +Supported query types: + +- `SELECT` +- `UPDATE` + +In most integrations, **SELECT queries are sufficient**. UPDATE queries should be used cautiously. + +--- + +# Query Parameters + +Queries use **Handlebars-style parameters**. + +Example: + +```sql +SELECT woo_oper, woo_qty_rep +FROM wo_oper +WHERE woo_nbr = '{{p:wonum}}' +``` + +`p:wonum` is provided by the client application. + +--- + +# Example Query Execution + +```vb +Dim args As New Dictionary(Of String, String) +args.Add("wonum", woNum) + +Dim rstDataAllOps = api.runQuery("Q-GET-WO-OPS", args) + +If rstDataAllOps.RecordCount > 0 Then + Do Until rstDataAllOps.EOF + Console.WriteLine(rstDataAllOps("woo_oper") & + " - QtyRep:" & + rstDataAllOps("woo_qty_rep")) + rstDataAllOps.MoveNext() + Loop +End If +``` + +The returned result set exposes a **recordset-style interface** with: + +- `RecordCount` +- `EOF` +- `MoveNext()` +- column access by name + +--- + +# Query Performance Guidelines + +For best performance: + +- Avoid returning large datasets +- Do not use `SELECT *` +- Specify explicit columns +- Limit queries to only the required data + +Recommended: + +```sql +SELECT pt_part, pt_desc +FROM pt_mstr +``` + +Avoid: + +```sql +SELECT * +FROM pt_mstr +``` + +--- + +# Example Architecture + +Typical integrations run as: + +- console applications +- background services +- device integration processes + +The application: + +1. Starts +2. Connects to OnRamp +3. Registers a socket +4. Waits for events +5. Processes events until shutdown + +Deployment models include: + +- one service handling all devices +- device-specific applications +- event-specific services + +--- + +# Future Expansion + +The SDK is designed to support additional integration capabilities including: + +- additional event channels +- expanded API functionality +- additional integration services + +--- + +# License + +Refer to the repository license for usage terms. diff --git a/prjOnRampAPI.sln b/prjOnRampAPI.sln new file mode 100644 index 0000000..4c68191 --- /dev/null +++ b/prjOnRampAPI.sln @@ -0,0 +1,27 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35707.178 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "prjOnRampAPI", "prjOnRampAPI\prjOnRampAPI.vbproj", "{392D651B-778A-4082-80D9-05FFFD1916A1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A95BA3CB-1B3C-4D31-8C3E-6AD39F4319EF}" + ProjectSection(SolutionItems) = preProject + README = README + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {392D651B-778A-4082-80D9-05FFFD1916A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {392D651B-778A-4082-80D9-05FFFD1916A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {392D651B-778A-4082-80D9-05FFFD1916A1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {392D651B-778A-4082-80D9-05FFFD1916A1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/prjOnRampAPI/Program.vb b/prjOnRampAPI/Program.vb new file mode 100644 index 0000000..e45b93c --- /dev/null +++ b/prjOnRampAPI/Program.vb @@ -0,0 +1,162 @@ +Imports System.Threading +Imports ORPublicApiRefCore +Imports ORSocketsCore +Module Program + + Private api As OnRampAPI + Private socketClient As ORSocketClient + Private quitEvent As ManualResetEvent = New ManualResetEvent(False) + + + Sub Main(args As String()) + + 'The http_root endpoint - use the MAIN webserver, not a farm + 'Use ProdCopy for testing if needed + Dim envUrl As String = "https://onramp.site.com/PROD" + + + 'create a socket connection to OnRamp Server to get interrupts of shop monitor events + 'You are registering as a "CLIENT APP NUMBER" - for this instance we will put SITE1001 + 'SITE1001 is the ID you set in S1280 - Device Shop Monitor for the SOCKETID on the event you wish to capture on this application. + socketClient = New ORSocketClient(envUrl, "SHOPMONITORCLIENT", "SITE1001") + + 'This is how to bind incoming Interrupt socket messages to a subroutine - kind of like a RS232 Interrupt + AddHandler socketClient.OnMessageReceived, AddressOf SocketMessageRec + + 'This is so we can debug websocket debugging info (for info purposes only, socket connected, reconnected etc) + AddHandler socketClient.OnLog, AddressOf SocketLog + + + 'Login to API to be able to query runQuery and Execute Queries in API Queries + 'You can do lots of things with the API like OpenScreens, change values - everything you can do with a dataloader you can do with the API + api = New OnRampAPI(envUrl) + + 'Make an OnRamp user to use to login with the api and login with those credentials + api.APILogin("api_user", "api_password") + + 'now the App waits forever and interrupts to SocketMessageRec + WaitForConsoleExit() + + + End Sub + + Private Sub SocketMessageRec(obj As Object, e As ORSocketMessageReceivedArgs) + 'This is the interrupt when the "shop monitor event happens" - it acts like RS232 sort of. + + Try + 'You get msg details like who sent it and when + Dim senderGroup = e.senderGroup + Dim senderName = e.senderName + Dim msgTime = e.msgTime + Dim msg = e.msg + + 'you get a message array + Dim msgSplit() As String + msgSplit = Split(msg, Convert.ToChar(175)) + + 'msg = deviceid, channelNum, eventType + + Dim deviceId As String = msgSplit(0) + Dim channelNum As Integer = V_N2I(msgSplit(1)) + Dim eventType As ShopMonitorChannelEvent = V_N2I(msgSplit(2)) + + 'you can tie into event types + Select Case eventType + Case ShopMonitorChannelEvent.WorkOrderLogin + 'new WO Logged in + Case ShopMonitorChannelEvent.WorkOrderLogout + 'WO Logged out + Case ShopMonitorChannelEvent.CycleStart + 'Cycle Start + Case ShopMonitorChannelEvent.CycleDone + 'Cycle Done + End Select + + '^^With this information you can get all you want to know about the work order etc + + 'We create a query in S5011 - API Queries to get device_channel Work Order Info, then use other queries to get operation information etc + 'The queries in this example are just for example, you need to create your own. + + 'arguments for server query Q-1234 + Dim rsChannelArgs As New Dictionary(Of String, String) + rsChannelArgs.Add("deviceid", deviceId) + rsChannelArgs.Add("devicechannel", channelNum) + + 'Example Q-1234 query is + 'SELECT devc_cur_wo FROM device_chan WHERE devc_device_id = '{{p:deviceid}}' AND devc_channel_num = '{{p:devicechannel}}' + Dim rstDataWo = api.runQuery("Q-1234", rsChannelArgs) + + If rstDataWo.RecordCount > 0 Then + Dim currentWoBarcode As String = rstDataWo("devc_cur_wo") + + 'get operation details + Dim rsOperArgs As New Dictionary(Of String, String) + rsOperArgs.Add("wobarcode", currentWoBarcode) + + 'Example Q-1235 + 'SELECT wom_partnum, wom_nbr, woo_oper FROM wo_oper INNER JOIN wo_mstr ON wom_nbr = woo_nbr where woo_barcode = '{{p:wobarcode}}' + Dim rstDataOp = api.runQuery("Q-1235", rsOperArgs) + If rstDataOp.RecordCount > 0 Then + Dim woNum As String = rstDataOp("wom_nbr") + Dim woOp As Integer = V_N2I(rstDataOp("woo_oper")) + Dim woPart As Integer = V_N2I(rstDataOp("wom_partnum")) + + 'get all operations for the WO + Dim allOpsArgs As New Dictionary(Of String, String) + allOpsArgs.Add("wonum", woNum) + + 'Example Q-1236 + 'SELECT woo_oper, woo_qty_rep FROM wo_oper WHERE woo_nbr = '{{p:wonum}}' + Dim rstDataAllOps = api.runQuery("Q-1236", allOpsArgs) + If rstDataAllOps.RecordCount > 0 Then + Do Until rstDataAllOps.EOF + Console.WriteLine(rstDataAllOps("woo_oper") + " - QtyRep:" & rstDataAllOps("woo_qty_rep")) + rstDataAllOps.MoveNext() + Loop + End If + + 'if we want to run an execute query + Dim argUpdate As New Dictionary(Of String, String) + argUpdate("deviceid") = deviceId + argUpdate("part") = woPart + + 'Example Q-1237 + 'UPDATE tbl_param_det SET PN = '{{p:part}}' WHERE Device = '{{p:deviceid}}' + api.runQuery("Q-1237", argUpdate) + + End If + End If + Catch ex As Exception + Dim err As String = ex.Message + End Try + + End Sub + + Private Sub SocketLog(obj As Object, msg As String) + Console.WriteLine(msg) + End Sub + + + Private Sub WaitForConsoleExit() + 'console app wait forevery on main thread + Console.WriteLine("Running. Press Ctrl+C to exit.") + AddHandler Console.CancelKeyPress, AddressOf ConsoleCancel + + + 'Block here until Ctrl+C + quitEvent.WaitOne() + + Console.WriteLine("Shutting down...") + Try + socketClient?.Dispose() + Catch + End Try + End Sub + + + Private Sub ConsoleCancel(sender, eArgs) + quitEvent.[Set]() + eArgs.Cancel = True + End Sub + +End Module diff --git a/prjOnRampAPI/modOHCommon.vb b/prjOnRampAPI/modOHCommon.vb new file mode 100644 index 0000000..b448cd6 --- /dev/null +++ b/prjOnRampAPI/modOHCommon.vb @@ -0,0 +1,53 @@ +Imports ORPublicApiRefCore + +Public Module modOHCommon + + + Private _rstData As List(Of KeyValuePair(Of String, DTRecordSet)) + Public Property rs(ByVal uniqueIdent As String) As DTRecordSet + Get + Return rstData(uniqueIdent) + End Get + Set(value As DTRecordSet) + rstData(uniqueIdent) = value + End Set + End Property + Public Property rstData(ByVal uniqueIdent As String) As DTRecordSet + Get + Dim localRs As List(Of KeyValuePair(Of String, DTRecordSet)) + + If _rstData Is Nothing Then + _rstData = New List(Of KeyValuePair(Of String, DTRecordSet)) + End If + localRs = _rstData + + For Each kvp As KeyValuePair(Of String, DTRecordSet) In localRs + If kvp.Key = uniqueIdent Then + Return kvp.Value + End If + Next kvp + 'Throw New Exception("rstData(" & uniqueIdent & ") could not be found") + Return Nothing + End Get + Set(ByVal value As DTRecordSet) + Dim localRs As List(Of KeyValuePair(Of String, DTRecordSet)) + + If _rstData Is Nothing Then + _rstData = New List(Of KeyValuePair(Of String, DTRecordSet)) + End If + localRs = _rstData + + 'remove from list if exists, then replace + For Each kvp As KeyValuePair(Of String, DTRecordSet) In localRs + If kvp.Key = uniqueIdent Then + localRs.Remove(kvp) + Exit For + End If + Next kvp + Dim wrapKVP As New KeyValuePair(Of String, DTRecordSet)(uniqueIdent, value) + localRs.Add(wrapKVP) + End Set + End Property + + +End Module diff --git a/prjOnRampAPI/nuget.config b/prjOnRampAPI/nuget.config new file mode 100644 index 0000000..6f18d8e --- /dev/null +++ b/prjOnRampAPI/nuget.config @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/prjOnRampAPI/prjOnRampAPI.vbproj b/prjOnRampAPI/prjOnRampAPI.vbproj new file mode 100644 index 0000000..0e6c689 --- /dev/null +++ b/prjOnRampAPI/prjOnRampAPI.vbproj @@ -0,0 +1,14 @@ + + + + Exe + prjOnRampAPI + net8.0 + + + + + + + +