├── .gitattributes ├── .github └── workflows │ ├── github-woopsa-create-nuget-package.yml │ ├── github-woopsa-deploy-nuget-package-preview.yml │ ├── github-woopsa-deploy-nuget-package.yml │ └── github-woopsa-test.yml ├── .gitignore ├── LICENSE.txt ├── README.md └── Sources ├── Angular └── ng-woopsa │ ├── .angular-cli.json │ ├── .editorconfig │ ├── .gitignore │ ├── README.md │ ├── dist.tgz │ ├── e2e │ ├── app.e2e-spec.ts │ ├── app.po.ts │ └── tsconfig.e2e.json │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── protractor.conf.js │ ├── public_api.ts │ ├── src │ ├── app │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ └── woopsa │ │ │ ├── services │ │ │ ├── woopsa.service.spec.ts │ │ │ ├── woopsa.service.ts │ │ │ └── woopsa │ │ │ │ ├── index.ts │ │ │ │ ├── lite-event.ts │ │ │ │ ├── woopsaClient.ts │ │ │ │ ├── woopsaInterfaces.ts │ │ │ │ ├── woopsaRequest.ts │ │ │ │ ├── woopsaType.ts │ │ │ │ └── woopsaValue.ts │ │ │ └── woopsa.module.ts │ ├── assets │ │ └── .gitkeep │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.css │ ├── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └── typings.d.ts │ ├── tsconfig.json │ ├── tslint.json │ └── yarn.lock ├── BeckhoffAds ├── README.txt └── WoopsaAds │ ├── App.config │ ├── App.xaml │ ├── App.xaml.cs │ ├── Commands │ ├── CloseWindowCommand.cs │ ├── CommandBase.cs │ ├── ShowDiagnosticWindowCommand.cs │ ├── ShowSampleWindowCommand.cs │ ├── StartServerCommand.cs │ └── StopServerCommand.cs │ ├── DateException.cs │ ├── DiagnosticWindow.xaml │ ├── DiagnosticWindow.xaml.cs │ ├── ExceptionViewer.xaml │ ├── ExceptionViewer.xaml.cs │ ├── Hardcodet.Wpf.TaskbarNotification.dll │ ├── Hardcodet.Wpf.TaskbarNotification.pdb │ ├── Icons │ ├── woopsa_24x24.ico │ └── woopsa_64x64.ico │ ├── Images │ ├── GreenLed.png │ ├── Play1Disabled.png │ ├── RedLed.png │ ├── Service24x24.png │ ├── Start.png │ ├── Stop.png │ ├── Supervision24x24.png │ ├── YellowLed.png │ ├── exit.png │ ├── woopsa.png │ └── woopsaIndustry4.0.png │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings │ ├── Web │ ├── WebWoopsa.css │ ├── WebWoopsa.js │ ├── index.html │ ├── jquery-1.12.1.min.js │ ├── woopsa-client.min.js │ └── woopsa.png │ ├── Woopsa.dll │ ├── Woopsa.pdb │ ├── WoopsaAds.csproj │ ├── WoopsaAdsController.cs │ └── WoopsaServer │ ├── BeckhoffValueType.cs │ ├── PlcParameter.cs │ ├── PlcStatus.cs │ ├── WoopsaAdsConfig.cs │ ├── WoopsaAdsProperty.cs │ └── WoopsaAdsServer.cs ├── DotNet ├── AssemblyVersionInfo.cs ├── Woopsa.sln ├── Woopsa │ ├── Client │ │ ├── WoopsaClient.cs │ │ ├── WoopsaClientMultiRequest.cs │ │ ├── WoopsaClientNotification.cs │ │ ├── WoopsaClientObject.cs │ │ ├── WoopsaClientProtocol.cs │ │ └── WoopsaClientSubscriptionChannel.cs │ ├── Definition │ │ ├── WoopsaException.cs │ │ ├── WoopsaExtensions.cs │ │ └── WoopsaInterfaces.cs │ ├── Dynamic │ │ ├── WoopsaDynamicClient.cs │ │ └── WoopsaJsonDataDynamic.cs │ ├── HTTPServer │ │ ├── HTML │ │ │ └── ErrorPage.html │ │ ├── HTTPServerUtils.cs │ │ ├── Processors │ │ │ ├── TlsProcessor.cs │ │ │ └── WWWAuthenticator.cs │ │ ├── RouteHandlers │ │ │ ├── RouteHandlerEmbeddedResources.cs │ │ │ ├── RouteHandlerFileSystem.cs │ │ │ ├── RouteHandlerMemory.cs │ │ │ └── RouteHandlerRedirect.cs │ │ ├── Routing │ │ │ ├── IHTTPRouteHandler.cs │ │ │ ├── Processors.cs │ │ │ ├── RouteMapper.cs │ │ │ └── RouteSolver.cs │ │ ├── Server │ │ │ ├── CustomThreadPool.cs │ │ │ └── WebServer.cs │ │ └── Utils │ │ │ ├── Constants.cs │ │ │ ├── Exceptions.cs │ │ │ ├── HTTPRequest.cs │ │ │ └── HTTPResponse.cs │ ├── Implementation │ │ ├── WoopsaJsonData.cs │ │ ├── WoopsaObject.cs │ │ ├── WoopsaObjectAdapter.cs │ │ └── WoopsaValue.cs │ ├── Protocol │ │ ├── WoopsaFormat.cs │ │ └── WoopsaServer.cs │ ├── Services │ │ ├── MultiRequest │ │ │ └── WoopsaMultiRequest.cs │ │ └── SubscriptionService │ │ │ ├── IWoopsaSubscriptionService.cs │ │ │ ├── WoopsaServerNotification.cs │ │ │ ├── WoopsaSubscriptionChannel.cs │ │ │ ├── WoopsaSubscriptionService.cs │ │ │ ├── WoopsaSubscriptionServiceImplementation.cs │ │ │ └── WoopsaSubscriptionServiceSubscription.cs │ ├── Utils │ │ ├── LightWeightTimer.cs │ │ ├── WoopsaReflection.cs │ │ ├── WoopsaTypeUtils.cs │ │ └── WoopsaUtils.cs │ └── Woopsa.csproj ├── WoopsaDemoClient │ ├── Program.cs │ └── WoopsaDemoClient.csproj ├── WoopsaDemoServer │ ├── Program.cs │ └── WoopsaDemoServer.csproj └── WoopsaTest │ ├── TestResources │ ├── EmbeddedResource │ │ ├── Class1.cs │ │ ├── EmbeddedResource.csproj │ │ └── Images │ │ │ ├── SubImages │ │ │ └── woopsa-logo.png │ │ │ └── woopsa-logo.png │ └── Woopsa.EmbeddedResource │ │ ├── Class1.cs │ │ ├── Images │ │ ├── SubImages │ │ │ └── woopsa-logo.png │ │ └── woopsa-logo.png │ │ └── Woopsa.EmbeddedResource.csproj │ ├── UnitTestClasses.cs │ ├── UnitTestHttpServer.cs │ ├── UnitTestReflection.cs │ ├── UnitTestWoopsaClient.cs │ ├── UnitTestWoopsaConverters.cs │ ├── UnitTestWoopsaDynamicNotification.cs │ ├── UnitTestWoopsaMultiRequest.cs │ ├── UnitTestWoopsaNotification.cs │ ├── UnitTestWoopsaObject.cs │ ├── UnitTestWoopsaObjectAdapter.cs │ ├── UnitTestWoopsaProtocol.cs │ ├── UnitTestWoopsaValue.cs │ └── WoopsaTest.csproj ├── Embedded ├── ArduinoDemoServer │ ├── ArduinoDemoServer.ino │ ├── html.h │ ├── index.html │ ├── woopsa-config.h │ ├── woopsa-server.c │ └── woopsa-server.h ├── DemoServer │ ├── DemoServer.c │ └── DemoServer.vcxproj ├── Server │ ├── Server.vcxproj │ ├── woopsa-config.h │ ├── woopsa-server.c │ └── woopsa-server.h ├── WoopsaEmbedded.sln └── build-windows.bat └── JavaScript ├── .gitignore ├── browser ├── demo │ ├── demo.js │ └── index.html ├── lib │ └── jquery-1.11.3.min.js └── woopsa-client.js └── nodejs ├── README.md ├── adapter.js ├── demos └── simple-server.js ├── exceptions.js ├── extensions ├── multi-request.js ├── subscription-channel.js └── subscription-service.js ├── index.js ├── package.json ├── reflector.js ├── server.js ├── types.js └── woopsa-utils.js /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/github-woopsa-create-nuget-package.yml: -------------------------------------------------------------------------------- 1 | name: Create Nuget Package Preview 2 | 3 | on: 4 | workflow_dispatch: 5 | branches: [ master, support/*, develop] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: windows-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: Setup .NET Core 2.2 16 | uses: actions/setup-dotnet@v1 17 | with: 18 | dotnet-version: 2.2.207 19 | 20 | - name: Setup .NET Core 3.1 21 | uses: actions/setup-dotnet@v1 22 | with: 23 | dotnet-version: 3.1.404 24 | 25 | - name: Generate Nuget Package 26 | run: dotnet pack ./Woopsa.sln --configuration Release --version-suffix preview.${{ github.run_number }} -o '${{ github.workspace }}/Sources/DotNet/artifacts' 27 | working-directory: ./Sources/DotNet 28 | 29 | - name: Upload a Build Artifact 30 | uses: actions/upload-artifact@v2.1.4 31 | with: 32 | name: Woopsa Nuget Package 33 | path: '${{ github.workspace }}/Sources/DotNet/artifacts/*.nupkg' 34 | if-no-files-found: error 35 | -------------------------------------------------------------------------------- /.github/workflows/github-woopsa-deploy-nuget-package-preview.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Nuget Package Preview 2 | 3 | on: 4 | workflow_dispatch: 5 | branches: [ master, support/*, develop] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: windows-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: Setup .NET Core 2.2 16 | uses: actions/setup-dotnet@v1 17 | with: 18 | dotnet-version: 2.2.207 19 | 20 | - name: Setup .NET Core 3.1 21 | uses: actions/setup-dotnet@v1 22 | with: 23 | dotnet-version: 3.1.404 24 | 25 | - name: Install dependencies 26 | run: dotnet restore ./Woopsa.sln 27 | working-directory: ./Sources/DotNet 28 | 29 | - name: Build 30 | run: dotnet build ./Woopsa.sln --configuration Release --no-restore 31 | working-directory: ./Sources/DotNet 32 | 33 | - name: Test 34 | run: dotnet test ./Woopsa.sln --no-restore --configuration Release --filter TestCategory!=Performance --verbosity normal 35 | working-directory: ./Sources/DotNet 36 | 37 | - name: Generate Nuget Package 38 | run: dotnet pack ./Woopsa.sln --configuration Release --version-suffix preview.${{ github.run_number }} -o '${{ github.workspace }}/Sources/DotNet/artifacts' 39 | working-directory: ./Sources/DotNet 40 | 41 | - name: Upload a Build Artifact 42 | uses: actions/upload-artifact@v2.1.4 43 | with: 44 | name: Woopsa Nuget Package 45 | path: '${{ github.workspace }}/Sources/DotNet/artifacts/*.nupkg' 46 | if-no-files-found: error 47 | 48 | - name: Deploy Nuget Package 49 | env: 50 | WOOPSA_NUGET_API_KEY: ${{ secrets.WOOPSA_NUGET_API_KEY }} 51 | run: dotnet nuget push '${{ github.workspace }}/Sources/DotNet/artifacts/**/*.nupkg' --api-key $env:WOOPSA_NUGET_API_KEY --source https://api.nuget.org/v3/index.json --no-symbols 52 | working-directory: ./Sources/DotNet 53 | -------------------------------------------------------------------------------- /.github/workflows/github-woopsa-deploy-nuget-package.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Nuget Package (Full) 2 | 3 | on: 4 | workflow_dispatch: 5 | branches: [ master, support/*, develop] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: windows-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: Setup .NET Core 2.2 16 | uses: actions/setup-dotnet@v1 17 | with: 18 | dotnet-version: 2.2.207 19 | 20 | - name: Setup .NET Core 3.1 21 | uses: actions/setup-dotnet@v1 22 | with: 23 | dotnet-version: 3.1.404 24 | 25 | - name: Install dependencies 26 | run: dotnet restore ./Woopsa.sln 27 | working-directory: ./Sources/DotNet 28 | 29 | - name: Build 30 | run: dotnet build ./Woopsa.sln --configuration Release --no-restore 31 | working-directory: ./Sources/DotNet 32 | 33 | - name: Test 34 | run: dotnet test ./Woopsa.sln --no-restore --configuration Release --filter TestCategory!=Performance --verbosity normal 35 | working-directory: ./Sources/DotNet 36 | 37 | - name: Generate Nuget Package 38 | run: dotnet pack ./Woopsa.sln --configuration Release -o '${{ github.workspace }}/Sources/DotNet/artifacts' 39 | working-directory: ./Sources/DotNet 40 | 41 | - name: Upload a Build Artifact 42 | uses: actions/upload-artifact@v2.1.4 43 | with: 44 | name: Woopsa Nuget Package 45 | path: '${{ github.workspace }}/Sources/DotNet/artifacts/*.nupkg' 46 | if-no-files-found: error 47 | 48 | - name: Deploy Nuget Package 49 | env: 50 | WOOPSA_NUGET_API_KEY: ${{ secrets.WOOPSA_NUGET_API_KEY }} 51 | run: dotnet nuget push '${{ github.workspace }}/Sources/DotNet/artifacts/**/*.nupkg' --api-key $env:WOOPSA_NUGET_API_KEY --source https://api.nuget.org/v3/index.json --no-symbols true 52 | working-directory: ./Sources/DotNet 53 | -------------------------------------------------------------------------------- /.github/workflows/github-woopsa-test.yml: -------------------------------------------------------------------------------- 1 | name: Launch unit tests 2 | 3 | on: 4 | workflow_dispatch: 5 | branches: [ master, support/*, develop] 6 | push: 7 | branches: [ master, support/*, develop] 8 | pull_request: 9 | branches: [ master, support/*, develop] 10 | 11 | jobs: 12 | build: 13 | 14 | runs-on: windows-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | 19 | - name: Setup .NET Core 2.2 20 | uses: actions/setup-dotnet@v1 21 | with: 22 | dotnet-version: 2.2.207 23 | 24 | - name: Setup .NET Core 3.1 25 | uses: actions/setup-dotnet@v1 26 | with: 27 | dotnet-version: 3.1.404 28 | 29 | - name: Install dependencies 30 | run: dotnet restore ./Woopsa.sln 31 | working-directory: ./Sources/DotNet 32 | 33 | - name: Build 34 | run: dotnet build ./Woopsa.sln --configuration Release --no-restore 35 | working-directory: ./Sources/DotNet 36 | 37 | - name: Test 38 | run: dotnet test ./Woopsa.sln --no-restore --configuration Release --filter TestCategory!=Performance --verbosity normal 39 | working-directory: ./Sources/DotNet 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # DotNet sources 2 | # User-specific files 3 | *.suo 4 | *.user 5 | *.userosscache 6 | *.sln.docstates 7 | 8 | # Build results 9 | [Dd]ebug/ 10 | [Dd]ebugPublic/ 11 | [Rr]elease/ 12 | [Rr]eleases/ 13 | x64/ 14 | x86/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | [Ll]og/ 19 | 20 | # .NET Core 21 | project.lock.json 22 | project.fragment.lock.json 23 | artifacts/ 24 | **/Properties/launchSettings.json 25 | 26 | # Visual Studio 2015 cache/options directory 27 | .vs/ 28 | 29 | # NuGet Packages 30 | *.nupkg 31 | # The packages folder can be ignored because of Package Restore 32 | **/packages/* 33 | # except build/, which is used as an MSBuild target. 34 | !**/packages/build/ 35 | # Uncomment if necessary however generally it will be regenerated when needed 36 | #!**/packages/repositories.config 37 | # NuGet v3's project.json files produces more ignorable files 38 | *.nuget.props 39 | *.nuget.targets 40 | 41 | # Others 42 | *.sdf 43 | *.filters 44 | *.log 45 | /Sources/Embedded/Release/* 46 | /Sources/Embedded/DemoServer/Release/* 47 | /Sources/Embedded/Server/Release/* 48 | node_modules 49 | Sources/DotNet/Woopsa/Properties/PublishProfiles 50 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Objectis SA 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Woopsa 2 | [![AppVeyor](https://img.shields.io/appveyor/ci/fabien-chevalley/woopsa.svg)](https://ci.appveyor.com/project/fabien-chevalley/woopsa) 3 | [![npm](https://img.shields.io/npm/v/woopsa.svg)](https://www.npmjs.com/package/woopsa) 4 | [![NuGet](https://img.shields.io/nuget/v/Woopsa.svg)](https://www.nuget.org/packages/Woopsa/) 5 | 6 | Woopsa is a protocol that's simple, lightweight, free, open-source, web and object-oriented, publish-subscribe, real-time capable and Industry 4.0 ready. It contributes to the revolution of the Internet of Things. 7 | 8 | Woopsa allows you to share the complete object model of your application in a way that's similar to OPC-UA. It's based on rock-solid foundations such as HTTP and JSON, which makes it work on the web out-of-the-box. Our mission is to get Woopsa on as many platforms as possible. Today, C# and JavaScript implementations exist, but there are much more to come! 9 | 10 | **Example JavaScript Client** 11 | 12 | ```javascript 13 | var woopsa = new WoopsaClient("http://demo.woopsa.org/woopsa", jQuery); 14 | woopsa.read("/Temperature", function(result){ 15 | //result = 24.2 16 | }); 17 | ``` 18 | 19 | 20 | **Example C# Server** 21 | 22 | ```csharp 23 | WeatherStation station = new WeatherStation(); 24 | WoopsaServer server = new WoopsaServer(station); 25 | 26 | station.Temperature = 24.2; 27 | ``` 28 | 29 | **Example node Server** 30 | ```javascript 31 | var woopsa = require('woopsa'); 32 | var weatherStation = { 33 | Temperature: 24.2, 34 | IsRaining: false, 35 | Sensitivity: 0.5, 36 | Altitude: 430, 37 | City: "Geneva", 38 | Time: new Date(), 39 | GetWeatherAtDate: function (date){ 40 | var date = new Date(date); 41 | if ( date.getDay() === 1 ) 42 | return "rainy"; 43 | else 44 | return "sunny"; 45 | }, 46 | Thermostat: { 47 | SetPoint: 24.0 48 | } 49 | } 50 | var server = new woopsa.Server(weatherStation, {port: 80}); 51 | ``` 52 | 53 | **Example Embedded C Server (Arduino, others)** 54 | 55 | ```c 56 | double Temperature; 57 | int Altitude; 58 | float GetAirPressure() { 59 | return 42.2; 60 | } 61 | 62 | WOOPSA_BEGIN(woopsa_entries) 63 | WOOPSA_PROPERTY_READONLY(Temperature, WOOPSA_TYPE_REAL) 64 | WOOPSA_PROPERTY(Altitude, WOOPSA_TYPE_INTEGER) 65 | WOOPSA_METHOD(GetAirPressure, WOOPSA_TYPE_REAL) 66 | WOOPSA_END 67 | ... 68 | ``` 69 | 70 | ## Getting the library 71 | The latest version is part of the git repository. It contains the .NET and JavaScript versions of the Woopsa library, as well as a few examples to get started! 72 | 73 | ## Getting started 74 | Our [Getting Started](http://www.woopsa.org/get-started/) tutorial allows you to get started quickly with Woopsa. It's really easy ! 75 | 76 | ## Compatibility 77 | Woopsa has been tested and works on: 78 | * .NET Framework 4.0 79 | * .NET Framework 4.5 80 | * .NET Core 2.0 81 | * Mono 4.0+ 82 | 83 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/.angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "project": { 4 | "name": "ng-woopsa" 5 | }, 6 | "apps": [ 7 | { 8 | "root": "src", 9 | "outDir": "dist", 10 | "assets": [ 11 | "assets", 12 | "favicon.ico" 13 | ], 14 | "index": "index.html", 15 | "main": "main.ts", 16 | "polyfills": "polyfills.ts", 17 | "test": "test.ts", 18 | "tsconfig": "tsconfig.app.json", 19 | "testTsconfig": "tsconfig.spec.json", 20 | "prefix": "app", 21 | "styles": [ 22 | "styles.css" 23 | ], 24 | "scripts": [], 25 | "environmentSource": "environments/environment.ts", 26 | "environments": { 27 | "dev": "environments/environment.ts", 28 | "prod": "environments/environment.prod.ts" 29 | } 30 | } 31 | ], 32 | "e2e": { 33 | "protractor": { 34 | "config": "./protractor.conf.js" 35 | } 36 | }, 37 | "lint": [ 38 | { 39 | "project": "src/tsconfig.app.json", 40 | "exclude": "**/node_modules/**" 41 | }, 42 | { 43 | "project": "src/tsconfig.spec.json", 44 | "exclude": "**/node_modules/**" 45 | }, 46 | { 47 | "project": "e2e/tsconfig.e2e.json", 48 | "exclude": "**/node_modules/**" 49 | } 50 | ], 51 | "test": { 52 | "karma": { 53 | "config": "./karma.conf.js" 54 | } 55 | }, 56 | "defaults": { 57 | "styleExt": "css", 58 | "component": {} 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /dist-server 6 | /tmp 7 | /out-tsc 8 | 9 | # dependencies 10 | /node_modules 11 | 12 | # IDEs and editors 13 | /.idea 14 | .project 15 | .classpath 16 | .c9/ 17 | *.launch 18 | .settings/ 19 | *.sublime-workspace 20 | 21 | # IDE - VSCode 22 | .vscode/* 23 | !.vscode/settings.json 24 | !.vscode/tasks.json 25 | !.vscode/launch.json 26 | !.vscode/extensions.json 27 | 28 | # misc 29 | /.sass-cache 30 | /connect.lock 31 | /coverage 32 | /libpeerconnection.log 33 | npm-debug.log 34 | testem.log 35 | /typings 36 | 37 | # e2e 38 | /e2e/*.js 39 | /e2e/*.map 40 | 41 | # System Files 42 | .DS_Store 43 | Thumbs.db 44 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/README.md: -------------------------------------------------------------------------------- 1 | # ng-woopsa 2 | This is the angular Woopsa module. To find out more about Woopsa and get the C# / Embedded C or jQuery implementation, go to http://www.woopsa.org! 3 | 4 | Woopsa is a protocol that's simple, lightweight, free, open-source, web and object-oriented, publish-subscribe, real-time capable and Industry 4.0 ready. It contributes to the revolution of the Internet of Things. 5 | 6 | Woopsa allows you to share the complete object model of your application in a way that's similar to OPC-UA. It's based on rock-solid foundations such as HTTP and JSON, which makes it work on the web out-of-the-box. Our mission is to get Woopsa on as many platforms as possible. Today, C# and JavaScript implementations exist, but there are much more to come! 7 | 8 | As a node module, Woopsa is very useful if you wish to quickly create a RESTful API. 9 | 10 | On the server-side, just give Woopsa any JavaScript object with properties and functions, and the library will create a RESTful API allowing you to read/write properties and call functions, automagically! 11 | 12 | ## Further help 13 | 14 | To get more help on Woopsa check our website 15 | (http://www.woopsa.org/). 16 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/dist.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woopsa-protocol/Woopsa/589b8cac3b70aab9cc07a85ca1d5414c0361f682/Sources/Angular/ng-woopsa/dist.tgz -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('ng-woopsa App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to app!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "jasminewd2", 11 | "node" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular/cli'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular/cli/plugins/karma') 14 | ], 15 | client:{ 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | reports: [ 'html', 'lcovonly' ], 20 | fixWebpackSourcePaths: true 21 | }, 22 | angularCli: { 23 | environment: 'dev' 24 | }, 25 | reporters: ['progress', 'kjhtml'], 26 | port: 9876, 27 | colors: true, 28 | logLevel: config.LOG_INFO, 29 | autoWatch: true, 30 | browsers: ['Chrome'], 31 | singleRun: false 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/ng-packagr/ng-package.schema.json", 3 | "lib": { 4 | "entryFile": "public_api.ts" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng-woopsa", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "description": "Woopsa is the first fully open-source object-oriented protocol for automation and the Internet of Things", 6 | "author": "Woopsa Protocol ", 7 | "bugs": { 8 | "url": "https://github.com/woopsa-protocol/Woopsa/issues" 9 | }, 10 | "homepage": "https://www.woopsa.org", 11 | "scripts": { 12 | "ng": "ng", 13 | "start": "ng serve", 14 | "build": "ng build --prod", 15 | "test": "ng test", 16 | "lint": "ng lint", 17 | "e2e": "ng e2e", 18 | "packagr": "ng-packagr -p ng-package.json" 19 | }, 20 | "private": false, 21 | "peerDependencies": { 22 | "@angular/animations": "^5.2.0", 23 | "@angular/common": "^5.2.0", 24 | "@angular/compiler": "^5.2.0", 25 | "@angular/core": "^5.2.0", 26 | "@angular/forms": "^5.2.0", 27 | "@angular/http": "^5.2.0", 28 | "@angular/platform-browser": "^5.2.0", 29 | "@angular/platform-browser-dynamic": "^5.2.0", 30 | "@angular/router": "^5.2.0", 31 | "core-js": "^2.4.1", 32 | "rxjs": "^5.5.6", 33 | "zone.js": "^0.8.19" 34 | }, 35 | "devDependencies": { 36 | "@angular/cli": "~1.7.0-beta.3", 37 | "@angular/compiler-cli": "^5.2.0", 38 | "@angular/language-service": "^5.2.0", 39 | "@types/jasmine": "~2.8.3", 40 | "@types/jasminewd2": "~2.0.2", 41 | "@types/node": "~6.0.60", 42 | "codelyzer": "^4.0.1", 43 | "jasmine-core": "~2.8.0", 44 | "jasmine-spec-reporter": "~4.2.1", 45 | "karma": "~2.0.0", 46 | "karma-chrome-launcher": "~2.2.0", 47 | "karma-coverage-istanbul-reporter": "^1.2.1", 48 | "karma-jasmine": "~1.1.0", 49 | "karma-jasmine-html-reporter": "^0.2.2", 50 | "ng-packagr": "^2.4.2", 51 | "protractor": "~5.1.2", 52 | "ts-node": "~4.1.0", 53 | "tslint": "~5.9.1", 54 | "typescript": "~2.5.3" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './e2e/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: 'e2e/tsconfig.e2e.json' 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/public_api.ts: -------------------------------------------------------------------------------- 1 | export * from './src/app/woopsa/woopsa.module'; 2 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woopsa-protocol/Woopsa/589b8cac3b70aab9cc07a85ca1d5414c0361f682/Sources/Angular/ng-woopsa/src/app/app.component.css -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

4 | Welcome to {{ title }}! 5 |

6 | Angular Logo 7 |
8 |

Here are some links to help you start:

9 | 20 | 21 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | describe('AppComponent', () => { 4 | beforeEach(async(() => { 5 | TestBed.configureTestingModule({ 6 | declarations: [ 7 | AppComponent 8 | ], 9 | }).compileComponents(); 10 | })); 11 | it('should create the app', async(() => { 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.debugElement.componentInstance; 14 | expect(app).toBeTruthy(); 15 | })); 16 | it(`should have as title 'app'`, async(() => { 17 | const fixture = TestBed.createComponent(AppComponent); 18 | const app = fixture.debugElement.componentInstance; 19 | expect(app.title).toEqual('app'); 20 | })); 21 | it('should render title in a h1 tag', async(() => { 22 | const fixture = TestBed.createComponent(AppComponent); 23 | fixture.detectChanges(); 24 | const compiled = fixture.debugElement.nativeElement; 25 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!'); 26 | })); 27 | }); 28 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'app'; 10 | } 11 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AppComponent } from './app.component'; 5 | 6 | @NgModule({ 7 | declarations: [ 8 | AppComponent 9 | ], 10 | imports: [ 11 | BrowserModule 12 | ], 13 | providers: [], 14 | bootstrap: [AppComponent] 15 | }) 16 | export class AppModule { } 17 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/app/woopsa/services/woopsa.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { WoopsaService } from './woopsa.service'; 4 | 5 | describe('WoopsaService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [WoopsaService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([WoopsaService], (service: WoopsaService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/app/woopsa/services/woopsa.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | 4 | import { 5 | WoopsaClient, WoopsaMetaResult, WoopsaValue, 6 | WoopsaSubscriptionChannel, WoopsaSubscription 7 | } from './woopsa'; 8 | 9 | @Injectable() 10 | export class WoopsaService { 11 | protected client: WoopsaClient; 12 | protected defaultChannel: WoopsaSubscriptionChannel; 13 | 14 | constructor(private http: HttpClient) { 15 | this.client = new WoopsaClient(http); 16 | this.defaultChannel = this.createChannel(200); 17 | } 18 | 19 | setUrl(path: string) { 20 | this.client.setUrl(path); 21 | } 22 | 23 | setAuthorization(username: string, password: string) { 24 | this.client.setAuthorization(username, password); 25 | } 26 | 27 | meta(path: string = ''): Promise { 28 | return this.client.meta(path); 29 | } 30 | 31 | read(path: string): Promise { 32 | return this.client.read(path); 33 | } 34 | 35 | write(path: string, value: any) { 36 | return this.client.write(path, value); 37 | } 38 | 39 | invoke(path: string, args: any = {}, forceNoArgsSerialize = false): Promise { 40 | return this.client.invoke(path, args, forceNoArgsSerialize); 41 | } 42 | 43 | createChannel(size: number): WoopsaSubscriptionChannel { 44 | return new WoopsaSubscriptionChannel(this.client, size); 45 | } 46 | 47 | subscribe(path: string, channel: WoopsaSubscriptionChannel = this.defaultChannel, 48 | monitorInterval: number = 0.02, publishInterval: number = 0.05): Promise { 49 | return this.client.subscribe(channel, path, monitorInterval, publishInterval); 50 | } 51 | 52 | unsubscribe(subscription: WoopsaSubscription, channel: WoopsaSubscriptionChannel = this.defaultChannel): Promise { 53 | return this.client.unsubscribe(channel, subscription); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/app/woopsa/services/woopsa/index.ts: -------------------------------------------------------------------------------- 1 | export * from './woopsaClient'; 2 | export * from './woopsaValue'; 3 | export * from './woopsaType'; 4 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/app/woopsa/services/woopsa/lite-event.ts: -------------------------------------------------------------------------------- 1 | export interface ILiteEvent { 2 | on(handler: (data?: T) => void): void; 3 | off(handler: (data?: T) => void): void; 4 | } 5 | 6 | export class LiteEvent implements ILiteEvent { 7 | private handlers: ((data?: T) => void)[] = []; 8 | 9 | public on(handler: (data?: T) => void): void { 10 | this.handlers.push(handler); 11 | } 12 | 13 | public off(handler: (data?: T) => void): void { 14 | this.handlers = this.handlers.filter(h => h !== handler); 15 | } 16 | 17 | public trigger(data?: T) { 18 | this.handlers.slice(0).forEach(h => h(data)); 19 | } 20 | 21 | public expose(): ILiteEvent { 22 | return this; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/app/woopsa/services/woopsa/woopsaInterfaces.ts: -------------------------------------------------------------------------------- 1 | import { WoopsaValueType } from './woopsaType'; 2 | 3 | export interface IWoopsaValue { 4 | asText: string; 5 | type: WoopsaValueType; 6 | timestamp: number; 7 | } 8 | 9 | export interface IWoopsaElement { 10 | owner: IWoopsaContainer; 11 | name: string; 12 | } 13 | 14 | export interface IWoopsaContainer extends IWoopsaElement { 15 | items: Iterable; 16 | } 17 | 18 | export interface IWoopsaProperty extends IWoopsaElement { 19 | isReadOnly: boolean; 20 | type: WoopsaValueType; 21 | getValue(): Promise; 22 | setValue(value: IWoopsaValue); 23 | } 24 | 25 | export interface IWoopsaMethodArgumentInfo { 26 | name: string; 27 | type: WoopsaValueType; 28 | } 29 | 30 | export interface IWoopsaMethod extends IWoopsaElement { 31 | returnType: WoopsaValueType; 32 | argumentInfos: Iterable; 33 | invoke(args: IWoopsaValue[]): IWoopsaValue; 34 | } 35 | 36 | export interface IWoopsaObject extends IWoopsaContainer { 37 | properties: Iterable; 38 | methods: Iterable; 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/app/woopsa/services/woopsa/woopsaRequest.ts: -------------------------------------------------------------------------------- 1 | import { HttpHeaders } from '@angular/common/http'; 2 | 3 | export class WoopsaRequest { 4 | constructor( 5 | private _url: string, 6 | private _verb: string, 7 | private _headers: HttpHeaders, 8 | private _body: string 9 | ) {} 10 | 11 | public get url() { 12 | return this._url; 13 | } 14 | 15 | public get verb() { 16 | return this._verb; 17 | } 18 | 19 | public get headers() { 20 | return this._headers; 21 | } 22 | 23 | public get body() { 24 | return this._body; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/app/woopsa/services/woopsa/woopsaType.ts: -------------------------------------------------------------------------------- 1 | 2 | export enum WoopsaValueType { 3 | Null, Logical, Integer, Real, DateTime, TimeSpan, Text, WoopsaLink, JsonData, ResourceUrl 4 | } 5 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/app/woopsa/services/woopsa/woopsaValue.ts: -------------------------------------------------------------------------------- 1 | import { WoopsaValueType } from './woopsaType'; 2 | import { IWoopsaValue } from './woopsaInterfaces'; 3 | 4 | // TODO: Add jsondata support 5 | export class WoopsaValue implements IWoopsaValue { 6 | asText: string; 7 | type: WoopsaValueType; 8 | timestamp: number; 9 | // private jsonData : WoopsaJsonData = null; 10 | 11 | constructor(text: string, type: WoopsaValueType, timestamp: number = NaN) { 12 | this.asText = text; 13 | this.type = type; 14 | this.timestamp = timestamp; 15 | 16 | // if(type == WoopsaValueType.JsonData) 17 | // jsonData = Woopsa.WoopsaJsonData.CreateFromText(text); 18 | } 19 | 20 | static CreateUnchecked(text: string, type: WoopsaValueType, timestamp: number = NaN): WoopsaValue { 21 | return new WoopsaValue(text, type, timestamp); 22 | } 23 | 24 | static CreateChecked(text: string, type: WoopsaValueType, timestamp: number = NaN): WoopsaValue { 25 | try { 26 | // TODO: realize check 27 | } catch (error) { } 28 | return WoopsaValue.CreateUnchecked(text, type, timestamp); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/app/woopsa/woopsa.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { HttpClientModule } from '@angular/common/http'; 4 | 5 | import { WoopsaService } from './services/woopsa.service'; 6 | 7 | @NgModule({ 8 | imports: [ 9 | CommonModule, 10 | HttpClientModule 11 | ], 12 | declarations: [], 13 | providers: [ WoopsaService ] 14 | }) 15 | export class WoopsaModule { } 16 | 17 | export * from './services/woopsa'; 18 | export { WoopsaService } from './services/woopsa.service'; 19 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woopsa-protocol/Woopsa/589b8cac3b70aab9cc07a85ca1d5414c0361f682/Sources/Angular/ng-woopsa/src/assets/.gitkeep -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false 8 | }; 9 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woopsa-protocol/Woopsa/589b8cac3b70aab9cc07a85ca1d5414c0361f682/Sources/Angular/ng-woopsa/src/favicon.ico -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NgWoopsa 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.log(err)); 13 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** IE10 and IE11 requires the following for the Reflect API. */ 41 | // import 'core-js/es6/reflect'; 42 | 43 | 44 | /** Evergreen browsers require these. **/ 45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 46 | import 'core-js/es7/reflect'; 47 | 48 | 49 | /** 50 | * Required to support Web Animations `@angular/platform-browser/animations`. 51 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 52 | **/ 53 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 54 | 55 | /** 56 | * By default, zone.js will patch all possible macroTask and DomEvents 57 | * user can disable parts of macroTask/DomEvents patch by setting following flags 58 | */ 59 | 60 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 61 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 62 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 63 | 64 | /* 65 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 66 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 67 | */ 68 | // (window as any).__Zone_enable_cross_context_check = true; 69 | 70 | /*************************************************************************************************** 71 | * Zone JS is required by default for Angular itself. 72 | */ 73 | import 'zone.js/dist/zone'; // Included with Angular CLI. 74 | 75 | 76 | 77 | /*************************************************************************************************** 78 | * APPLICATION IMPORTS 79 | */ 80 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | "module": "es2015", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "types": [ 8 | "jasmine", 9 | "node" 10 | ] 11 | }, 12 | "files": [ 13 | "test.ts" 14 | ], 15 | "include": [ 16 | "**/*.spec.ts", 17 | "**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var module: NodeModule; 3 | interface NodeModule { 4 | id: string; 5 | } 6 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "target": "es5", 11 | "typeRoots": [ 12 | "node_modules/@types" 13 | ], 14 | "lib": [ 15 | "es2017", 16 | "dom" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/Angular/ng-woopsa/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs", 22 | "rxjs/Rx" 23 | ], 24 | "import-spacing": true, 25 | "indent": [ 26 | true, 27 | "spaces" 28 | ], 29 | "interface-over-type-literal": true, 30 | "label-position": true, 31 | "max-line-length": [ 32 | true, 33 | 140 34 | ], 35 | "member-access": false, 36 | "member-ordering": [ 37 | true, 38 | { 39 | "order": [ 40 | "static-field", 41 | "instance-field", 42 | "static-method", 43 | "instance-method" 44 | ] 45 | } 46 | ], 47 | "no-arg": true, 48 | "no-bitwise": true, 49 | "no-console": [ 50 | true, 51 | "debug", 52 | "info", 53 | "time", 54 | "timeEnd", 55 | "trace" 56 | ], 57 | "no-construct": true, 58 | "no-debugger": true, 59 | "no-duplicate-super": true, 60 | "no-empty": false, 61 | "no-empty-interface": true, 62 | "no-eval": true, 63 | "no-inferrable-types": [ 64 | true, 65 | "ignore-params" 66 | ], 67 | "no-misused-new": true, 68 | "no-non-null-assertion": true, 69 | "no-shadowed-variable": true, 70 | "no-string-literal": false, 71 | "no-string-throw": true, 72 | "no-switch-case-fall-through": true, 73 | "no-trailing-whitespace": true, 74 | "no-unnecessary-initializer": true, 75 | "no-unused-expression": true, 76 | "no-use-before-declare": true, 77 | "no-var-keyword": true, 78 | "object-literal-sort-keys": false, 79 | "one-line": [ 80 | true, 81 | "check-open-brace", 82 | "check-catch", 83 | "check-else", 84 | "check-whitespace" 85 | ], 86 | "prefer-const": true, 87 | "quotemark": [ 88 | true, 89 | "single" 90 | ], 91 | "radix": true, 92 | "semicolon": [ 93 | true, 94 | "always" 95 | ], 96 | "triple-equals": [ 97 | true, 98 | "allow-null-check" 99 | ], 100 | "typedef-whitespace": [ 101 | true, 102 | { 103 | "call-signature": "nospace", 104 | "index-signature": "nospace", 105 | "parameter": "nospace", 106 | "property-declaration": "nospace", 107 | "variable-declaration": "nospace" 108 | } 109 | ], 110 | "unified-signatures": true, 111 | "variable-name": false, 112 | "whitespace": [ 113 | true, 114 | "check-branch", 115 | "check-decl", 116 | "check-operator", 117 | "check-separator", 118 | "check-type" 119 | ], 120 | "directive-selector": [ 121 | true, 122 | "attribute", 123 | "app", 124 | "camelCase" 125 | ], 126 | "component-selector": [ 127 | true, 128 | "element", 129 | "app", 130 | "kebab-case" 131 | ], 132 | "no-output-on-prefix": true, 133 | "use-input-property-decorator": true, 134 | "use-output-property-decorator": true, 135 | "use-host-property-decorator": true, 136 | "no-input-rename": true, 137 | "no-output-rename": true, 138 | "use-life-cycle-interface": true, 139 | "use-pipe-transform-interface": true, 140 | "component-class-suffix": true, 141 | "directive-class-suffix": true 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /Sources/BeckhoffAds/README.txt: -------------------------------------------------------------------------------- 1 | The WoopsaAds App is a gateway between ADS and Woopsa. 2 | It is a woopsa server that connects to 1 or more ADS server (PLC Beckhoff). 3 | The woopsa server publish all data he found on the ADS server. 4 | 5 | 6 | !!!!!!!!!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!!!!!!!!! 7 | 8 | 1/ TwinCAT must be installed on your machine if it's not a PLC Beckhoff. 9 | To download it, you have to use the TwinCAT installer that you can find on the site: www.beckhoff.com 10 | 1.1/ Download the last version of TwinCAT in : Download -> Software -> TwinCAT 3 -> TE1xxx | Engineering 11 | 1.2/ Install it on your machine 12 | 13 | 2/ The "TwinCAT.Ads.dll" library is used in the project, but it must be added to source code 14 | 2.1/ Copy the TwinCAT.Ads.dll from "C:\TwinCAT\AdsApi\.NET\v4.0" to the root of the WoopsaAds project. 15 | 16 | 17 | 3/ You need to have Framework .NET 4.0 minimum. If you don't have it, install it with "dotNetFx40_Full_x86_x64.exe". 18 | You can download this .exe at : https://www.microsoft.com/en-us/download/details.aspx?id=17718 19 | 20 | 21 | 22 | For each server, there is need a Route. 23 | You can add or remove Static Route on right click on the TwinCAT icon in the traybar -> Router -> Routes editieren 24 | For add : 1/ Do a Broadcast Search 25 | 2/ Select your PLC in the list 26 | 3/ Check the "IP Address" radio button in "Address Info" 27 | 4/ Click on "Add Route" 28 | 5/ Enter "1" for the Password and click OK 29 | 30 | -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Windows; 3 | using System.Windows.Threading; 4 | 5 | namespace WoopsaAds 6 | { 7 | /// 8 | /// Interaction logic for App.xaml 9 | /// 10 | public partial class App : Application 11 | { 12 | public WoopsaAdsController controller; 13 | 14 | public static object appLock = new object(); 15 | public static bool isExiting = false; 16 | protected override void OnStartup(StartupEventArgs e) 17 | { 18 | Current.DispatcherUnhandledException += OnDispatcherUnhandledException; 19 | base.OnStartup(e); 20 | controller = new WoopsaAdsController(); 21 | if (controller.runAtStartUp) 22 | Thread.Sleep(5000); 23 | MainWindow = new MainWindow(controller); 24 | ((MainWindow)MainWindow).InitWindow(); 25 | controller.Load(); 26 | } 27 | 28 | public void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) 29 | { 30 | string errorMessage = string.Format("An unhandled exception occurred: {0}", e.Exception.Message); 31 | MessageBox.Show(errorMessage, "Error", MessageBoxButton.OK, MessageBoxImage.Error); 32 | e.Handled = true; 33 | ((MainWindow)MainWindow).diagnostic.AddException(e.Exception); 34 | } 35 | 36 | protected override void OnExit(ExitEventArgs e) 37 | { 38 | base.OnExit(e); 39 | lock (appLock) 40 | { 41 | isExiting = true; 42 | } 43 | controller.ShutDown(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/Commands/CloseWindowCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Input; 2 | 3 | namespace WoopsaAds.Commands 4 | { 5 | /// 6 | /// Closes the current window. 7 | /// 8 | public class CloseWindowCommand : CommandBase 9 | { 10 | public override void Execute(object parameter) 11 | { 12 | GetTaskbarWindow(parameter).diagnostic.CloseWindow(); 13 | GetTaskbarWindow(parameter).CloseWindow(); 14 | CommandManager.InvalidateRequerySuggested(); 15 | } 16 | 17 | public override bool CanExecute(object parameter) 18 | { 19 | return true; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/Commands/ShowDiagnosticWindowCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Input; 2 | 3 | namespace WoopsaAds.Commands 4 | { 5 | /// 6 | /// Shows the main window. 7 | /// 8 | public class ShowDiagnosticWindowCommand : CommandBase 9 | { 10 | public override void Execute(object parameter) 11 | { 12 | GetTaskbarWindow(parameter).diagnostic.Show(); 13 | CommandManager.InvalidateRequerySuggested(); 14 | GetTaskbarWindow(parameter).diagnostic.Focus(); 15 | } 16 | 17 | public override bool CanExecute(object parameter) 18 | { 19 | return true; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/Commands/ShowSampleWindowCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Input; 2 | 3 | namespace WoopsaAds.Commands 4 | { 5 | /// 6 | /// Shows the main window. 7 | /// 8 | public class ShowSampleWindowCommand : CommandBase 9 | { 10 | public override void Execute(object parameter) 11 | { 12 | GetTaskbarWindow(parameter).Show(); 13 | CommandManager.InvalidateRequerySuggested(); 14 | GetTaskbarWindow(parameter).Focus(); 15 | } 16 | 17 | public override bool CanExecute(object parameter) 18 | { 19 | return true; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/Commands/StartServerCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Input; 2 | 3 | namespace WoopsaAds.Commands 4 | { 5 | /// 6 | /// Hides the main window. 7 | /// 8 | public class StartServerCommand : CommandBase 9 | { 10 | public override void Execute(object parameter) 11 | { 12 | (GetTaskbarWindow(parameter)).controller.Restart(); 13 | CommandManager.InvalidateRequerySuggested(); 14 | } 15 | 16 | public override bool CanExecute(object parameter) 17 | { 18 | MainWindow win = GetTaskbarWindow(parameter); 19 | return win != null && !win.controller.isRunning; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/Commands/StopServerCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Input; 2 | 3 | namespace WoopsaAds.Commands 4 | { 5 | /// 6 | /// Hides the main window. 7 | /// 8 | public class StopServerCommand : CommandBase 9 | { 10 | public override void Execute(object parameter) 11 | { 12 | (GetTaskbarWindow(parameter)).controller.ShutDown(); 13 | CommandManager.InvalidateRequerySuggested(); 14 | } 15 | 16 | public override bool CanExecute(object parameter) 17 | { 18 | MainWindow win = GetTaskbarWindow(parameter); 19 | return win != null && win.controller.isRunning; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/DateException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace WoopsaAds 4 | { 5 | public class DateException 6 | { 7 | public Exception exception { get; set; } 8 | public DateTime arrivalTime { get; set; } 9 | 10 | public DateException(Exception ex, DateTime dateTime) 11 | { 12 | exception = ex; 13 | arrivalTime = dateTime; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/DiagnosticWindow.xaml: -------------------------------------------------------------------------------- 1 |  15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/ExceptionViewer.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 24 | 25 | 28 | 32 | 36 | 39 | 40 | 43 | 44 | 45 | 46 | 47 | 48 | 57 | 24 | 25 | 26 |
27 | 28 |

Write value

29 | 30 | 31 |
32 | 33 |

34 | 35 | 36 |
37 | 38 |

39 | 40 |
41 | 42 | 43 |

Subscribe variable

44 | 45 | 46 |
47 | 48 |

49 | 50 | 51 | 52 | 53 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/Web/woopsa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woopsa-protocol/Woopsa/589b8cac3b70aab9cc07a85ca1d5414c0361f682/Sources/BeckhoffAds/WoopsaAds/Web/woopsa.png -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/Woopsa.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woopsa-protocol/Woopsa/589b8cac3b70aab9cc07a85ca1d5414c0361f682/Sources/BeckhoffAds/WoopsaAds/Woopsa.dll -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/Woopsa.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woopsa-protocol/Woopsa/589b8cac3b70aab9cc07a85ca1d5414c0361f682/Sources/BeckhoffAds/WoopsaAds/Woopsa.pdb -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/WoopsaServer/BeckhoffValueType.cs: -------------------------------------------------------------------------------- 1 | namespace WoopsaAds 2 | { 3 | public static class BeckhoffValueType 4 | { 5 | public const string BOOL = "BOOL"; 6 | public const string BYTE = "BYTE"; 7 | public const string WORD = "WORD"; 8 | public const string DWORD = "DWORD"; 9 | public const string SINT = "SINT"; 10 | public const string INT = "INT"; 11 | public const string DINT = "DINT"; 12 | public const string LINT = "LINT"; 13 | public const string USINT = "USINT"; 14 | public const string UINT = "UINT"; 15 | public const string UDINT = "UDINT"; 16 | public const string ULINT = "ULINT"; 17 | public const string REAL = "REAL"; 18 | public const string LREAL = "LREAL"; 19 | public const string STRING = "STRING(80)"; 20 | public const string TIME = "TIME"; 21 | public const string TIME_OF_DAY = "TIME_OF_DAY"; 22 | public const string DATE = "DATE"; 23 | public const string DATE_AND_TIME = "DATE_AND_TIME"; 24 | } 25 | } -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/WoopsaServer/PlcParameter.cs: -------------------------------------------------------------------------------- 1 | namespace WoopsaAds 2 | { 3 | public class PlcParameter 4 | { 5 | public string name { get; set; } 6 | public string adsNetId { get; set; } 7 | 8 | public PlcParameter(string name, string adsNetId) 9 | { 10 | this.name = name; 11 | this.adsNetId = adsNetId; 12 | } 13 | 14 | public PlcParameter() 15 | { 16 | name = "plc"; 17 | adsNetId = "127.0.0.1.1.1"; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/WoopsaServer/PlcStatus.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace WoopsaAds 3 | { 4 | public class PlcStatus 5 | { 6 | public string plcName { get; set; } 7 | 8 | public bool status { get; set; } 9 | 10 | public string statusName { get; set; } 11 | 12 | public string statusLedPath 13 | { 14 | get 15 | { 16 | if (status) 17 | { 18 | return "/Images/GreenLed.png"; 19 | } 20 | else 21 | { 22 | if(statusName == "Stop") 23 | return "/Images/YellowLed.png"; 24 | else 25 | return "/Images/RedLed.png"; 26 | } 27 | } 28 | } 29 | 30 | public PlcStatus(string name, bool status,string statusName) 31 | { 32 | plcName = name; 33 | this.status = status; 34 | this.statusName = statusName; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/WoopsaServer/WoopsaAdsConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | 3 | namespace WoopsaAds 4 | { 5 | public class WoopsaAdsConfig 6 | { 7 | public int port { get; set; } 8 | public string folderPathWebPages { get; set; } 9 | public bool isLocal { get; set; } 10 | public bool runAtStartUp { get; set; } 11 | public ObservableCollection plcParameterList { get; set; } 12 | 13 | public WoopsaAdsConfig(int port, string folderPathWebPages, bool isLocal, bool runAtStartUp, ObservableCollection plcParameterList) 14 | { 15 | this.port = port; 16 | this.folderPathWebPages = folderPathWebPages; 17 | this.isLocal = isLocal; 18 | this.runAtStartUp = runAtStartUp; 19 | this.plcParameterList = plcParameterList; 20 | } 21 | 22 | public WoopsaAdsConfig() 23 | { 24 | port = 80; 25 | folderPathWebPages = ""; 26 | isLocal = true; 27 | runAtStartUp = true; 28 | plcParameterList = new ObservableCollection(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/BeckhoffAds/WoopsaAds/WoopsaServer/WoopsaAdsProperty.cs: -------------------------------------------------------------------------------- 1 | using TwinCAT.Ads; 2 | using Woopsa; 3 | 4 | namespace WoopsaAds 5 | { 6 | public class WoopsaAdsProperty : WoopsaProperty 7 | { 8 | public string RootName { get; private set; } 9 | public TcAdsSymbolInfo AdsInfo { get; private set; } 10 | public WoopsaAdsProperty(WoopsaObject container, string name, WoopsaValueType type, WoopsaPropertyGet get, TcAdsSymbolInfo adsInfo) : 11 | this(container, name, type, get, null, adsInfo) 12 | { 13 | 14 | } 15 | public WoopsaAdsProperty(WoopsaObject container, string name, WoopsaValueType type, WoopsaPropertyGet get, WoopsaPropertySet set, TcAdsSymbolInfo adsInfo) : 16 | base(container, name, type, get, set) 17 | { 18 | string[] path = adsInfo.Name.Split('.'); 19 | RootName = path[0]; 20 | AdsInfo = adsInfo; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/DotNet/AssemblyVersionInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | 4 | [assembly: AssemblyCompany("Objectis SA")] 5 | [assembly: AssemblyProduct("Woopsa")] 6 | [assembly: AssemblyCopyright("Copyright © Objectis SA 2016")] 7 | [assembly: AssemblyVersion("1.2.3")] 8 | [assembly: AssemblyFileVersion("1.2.3")] -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30320.27 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Woopsa", "Woopsa\Woopsa.csproj", "{4F3CA53C-98F2-4EEB-900C-8855F4081614}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WoopsaDemoClient", "WoopsaDemoClient\WoopsaDemoClient.csproj", "{7868F636-9118-4D2D-A77A-7D96D43CDE9A}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WoopsaDemoServer", "WoopsaDemoServer\WoopsaDemoServer.csproj", "{2D61F67D-7840-4515-9F2C-AB788ED9B580}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{16EEE752-C36C-4190-AEA6-EDB0B68B362B}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WoopsaTest", "WoopsaTest\WoopsaTest.csproj", "{2F880D14-F548-4A6C-B002-539FCF070194}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EmbeddedResource", "WoopsaTest\TestResources\EmbeddedResource\EmbeddedResource.csproj", "{B30437A0-BA1C-42AA-A40D-60DC3689016A}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Woopsa.EmbeddedResource", "WoopsaTest\TestResources\Woopsa.EmbeddedResource\Woopsa.EmbeddedResource.csproj", "{512E7B24-3D31-4DD5-A7E9-BF644A6DC06F}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {4F3CA53C-98F2-4EEB-900C-8855F4081614}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {4F3CA53C-98F2-4EEB-900C-8855F4081614}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {4F3CA53C-98F2-4EEB-900C-8855F4081614}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {4F3CA53C-98F2-4EEB-900C-8855F4081614}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {7868F636-9118-4D2D-A77A-7D96D43CDE9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {7868F636-9118-4D2D-A77A-7D96D43CDE9A}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {7868F636-9118-4D2D-A77A-7D96D43CDE9A}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {7868F636-9118-4D2D-A77A-7D96D43CDE9A}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {2D61F67D-7840-4515-9F2C-AB788ED9B580}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {2D61F67D-7840-4515-9F2C-AB788ED9B580}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {2D61F67D-7840-4515-9F2C-AB788ED9B580}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {2D61F67D-7840-4515-9F2C-AB788ED9B580}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {2F880D14-F548-4A6C-B002-539FCF070194}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {2F880D14-F548-4A6C-B002-539FCF070194}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {2F880D14-F548-4A6C-B002-539FCF070194}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {2F880D14-F548-4A6C-B002-539FCF070194}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {B30437A0-BA1C-42AA-A40D-60DC3689016A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {B30437A0-BA1C-42AA-A40D-60DC3689016A}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {B30437A0-BA1C-42AA-A40D-60DC3689016A}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {B30437A0-BA1C-42AA-A40D-60DC3689016A}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {512E7B24-3D31-4DD5-A7E9-BF644A6DC06F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {512E7B24-3D31-4DD5-A7E9-BF644A6DC06F}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {512E7B24-3D31-4DD5-A7E9-BF644A6DC06F}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {512E7B24-3D31-4DD5-A7E9-BF644A6DC06F}.Release|Any CPU.Build.0 = Release|Any CPU 50 | EndGlobalSection 51 | GlobalSection(SolutionProperties) = preSolution 52 | HideSolutionNode = FALSE 53 | EndGlobalSection 54 | GlobalSection(NestedProjects) = preSolution 55 | {2F880D14-F548-4A6C-B002-539FCF070194} = {16EEE752-C36C-4190-AEA6-EDB0B68B362B} 56 | {B30437A0-BA1C-42AA-A40D-60DC3689016A} = {16EEE752-C36C-4190-AEA6-EDB0B68B362B} 57 | {512E7B24-3D31-4DD5-A7E9-BF644A6DC06F} = {16EEE752-C36C-4190-AEA6-EDB0B68B362B} 58 | EndGlobalSection 59 | GlobalSection(ExtensibilityGlobals) = postSolution 60 | SolutionGuid = {26BA5D0A-D208-4C86-A525-F9847A589A4C} 61 | EndGlobalSection 62 | EndGlobal 63 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/Client/WoopsaClientNotification.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Woopsa 4 | { 5 | public class WoopsaClientNotification : IWoopsaNotification 6 | { 7 | public WoopsaClientNotification(WoopsaValue value, int subscriptionId) 8 | { 9 | Value = value; 10 | SubscriptionId = subscriptionId; 11 | } 12 | 13 | public WoopsaValue Value { get; } 14 | 15 | IWoopsaValue IWoopsaNotification.Value => Value; 16 | 17 | public int SubscriptionId { get; } 18 | 19 | public int Id { get; set; } 20 | 21 | } 22 | 23 | public class WoopsaClientNotifications : IWoopsaNotifications 24 | { 25 | public WoopsaClientNotifications() 26 | { 27 | _notifications = new List(); 28 | } 29 | 30 | public void Add(WoopsaClientNotification notification) => _notifications.Add(notification); 31 | 32 | public IEnumerable Notifications => _notifications; 33 | 34 | private readonly List _notifications; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/Definition/WoopsaException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Woopsa 8 | { 9 | public static class WoopsaExceptionMessage 10 | { 11 | public static string WoopsaCastTypeMessage(string destinationType, string sourceType) 12 | { 13 | return string.Format("Cannot typecast woopsa value of type {0} to type {1}", sourceType, destinationType); 14 | } 15 | 16 | public static string WoopsaCastValueMessage(string destinationType, string sourceValue) 17 | { 18 | return string.Format("Cannot typecast woopsa value {0} to type {1}", sourceValue, destinationType); 19 | } 20 | 21 | public static string WoopsaElementNotFoundMessage(string path) 22 | { 23 | return string.Format("Cannot find WoopsaElement specified by path {0}", path); 24 | } 25 | } 26 | 27 | [Serializable] 28 | public class WoopsaException: Exception 29 | { 30 | public WoopsaException() 31 | { 32 | } 33 | public WoopsaException(string message) 34 | : base(message) 35 | { 36 | } 37 | public WoopsaException(string message, Exception innerException) 38 | : base(message, innerException) 39 | { 40 | } 41 | } 42 | 43 | [Serializable] 44 | public class WoopsaNotificationsLostException : WoopsaException 45 | { 46 | public WoopsaNotificationsLostException() 47 | { 48 | } 49 | public WoopsaNotificationsLostException(string message) 50 | : base(message) 51 | { 52 | } 53 | public WoopsaNotificationsLostException(string message, Exception innerException) : base(message, innerException) 54 | { 55 | } 56 | } 57 | 58 | [Serializable] 59 | public class WoopsaInvalidSubscriptionChannelException : WoopsaException 60 | { 61 | public WoopsaInvalidSubscriptionChannelException() 62 | { 63 | } 64 | public WoopsaInvalidSubscriptionChannelException(string message) 65 | : base(message) 66 | { 67 | } 68 | public WoopsaInvalidSubscriptionChannelException(string message, Exception innerException) 69 | : base(message, innerException) 70 | { 71 | } 72 | } 73 | 74 | [Serializable] 75 | public class WoopsaNotFoundException : WoopsaException 76 | { 77 | public WoopsaNotFoundException() 78 | { 79 | } 80 | public WoopsaNotFoundException(string message) 81 | : base(message) 82 | { 83 | } 84 | public WoopsaNotFoundException(string message, Exception innerException) 85 | : base(message, innerException) 86 | { 87 | } 88 | } 89 | 90 | [Serializable] 91 | public class WoopsaInvalidOperationException : WoopsaException 92 | { 93 | public WoopsaInvalidOperationException() 94 | { 95 | } 96 | public WoopsaInvalidOperationException(string message) 97 | : base(message) 98 | { 99 | } 100 | public WoopsaInvalidOperationException(string message, Exception innerException) 101 | : base(message, innerException) 102 | { 103 | } 104 | } 105 | 106 | public sealed class ExceptionEventArgs : EventArgs 107 | { 108 | public ExceptionEventArgs(Exception exception) 109 | { 110 | Exception = exception; 111 | } 112 | 113 | public Exception Exception { get; } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/Definition/WoopsaInterfaces.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Woopsa 8 | { 9 | 10 | public static class WoopsaConst 11 | { 12 | public const char UrlSeparator = '/'; 13 | public const char WoopsaPathSeparator = UrlSeparator; 14 | public const char WoopsaLinkSeparator = '#'; 15 | public const string WoopsaRootPath = "/"; 16 | 17 | public const string WoopsaFalse = "false"; 18 | public const string WoopsaTrue = "true"; 19 | public const string WoopsaNull = "null"; 20 | } 21 | 22 | public enum WoopsaValueType 23 | { 24 | Null, Logical, Integer, Real, DateTime, TimeSpan, Text, WoopsaLink, JsonData, ResourceUrl 25 | } 26 | 27 | public interface IWoopsaValue 28 | { 29 | string AsText { get; } 30 | WoopsaValueType Type { get; } 31 | DateTime? TimeStamp { get; } 32 | } 33 | 34 | public interface IWoopsaElement 35 | { 36 | /// Is null for root nodes 37 | IWoopsaContainer Owner { get; } 38 | 39 | string Name { get; } 40 | } 41 | 42 | public interface IWoopsaContainer : IWoopsaElement 43 | { 44 | IEnumerable Items { get; } 45 | } 46 | 47 | public interface IWoopsaProperty : IWoopsaElement 48 | { 49 | bool IsReadOnly { get; } 50 | IWoopsaValue Value { get; set; } 51 | WoopsaValueType Type { get; } 52 | } 53 | 54 | public interface IWoopsaMethodArgumentInfo 55 | { 56 | string Name { get; } 57 | WoopsaValueType Type { get; } 58 | } 59 | 60 | public interface IWoopsaMethod : IWoopsaElement 61 | { 62 | IWoopsaValue Invoke(IWoopsaValue[] arguments); 63 | WoopsaValueType ReturnType { get; } 64 | IEnumerable ArgumentInfos { get; } 65 | } 66 | 67 | public interface IWoopsaObject : IWoopsaContainer 68 | { 69 | IEnumerable Properties { get; } 70 | IEnumerable Methods { get; } 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/Dynamic/WoopsaJsonDataDynamic.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Dynamic; 3 | 4 | namespace Woopsa 5 | { 6 | public class WoopsaJsonDataDynamic : DynamicObject 7 | { 8 | #region Constructors 9 | 10 | public WoopsaJsonDataDynamic(WoopsaJsonData data) 11 | { 12 | _data = data; 13 | } 14 | 15 | #endregion 16 | 17 | #region Public Override Methods 18 | 19 | public override bool TryGetMember(GetMemberBinder binder, out object result) 20 | { 21 | try 22 | { 23 | if (_data[binder.Name].IsSimple) 24 | result = _data[binder.Name]; 25 | else 26 | result = new WoopsaJsonDataDynamic(_data[binder.Name]); 27 | return true; 28 | } 29 | catch (Exception) 30 | { 31 | result = null; 32 | return false; 33 | } 34 | } 35 | 36 | public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) 37 | { 38 | try 39 | { 40 | if (_data[(int)indexes[0]].IsSimple) 41 | result = _data[(int)indexes[0]]; 42 | else 43 | result = new WoopsaJsonDataDynamic(_data[(int)indexes[0]]); 44 | return true; 45 | } 46 | catch (Exception) 47 | { 48 | result = null; 49 | return false; 50 | } 51 | } 52 | 53 | #endregion 54 | 55 | #region Private Members 56 | 57 | private readonly WoopsaJsonData _data; 58 | 59 | #endregion 60 | } 61 | 62 | public static class WoopsaJsonDataDynamicExtensions 63 | { 64 | public static WoopsaJsonDataDynamic ToDynamic(this WoopsaJsonData data) 65 | { 66 | return new WoopsaJsonDataDynamic(data); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/HTTPServer/HTML/ErrorPage.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | @ErrorCode Error 7 | 8 | 22 | 23 | 24 |
25 |

An error occured while processing the request.

26 |

Error @ErrorCode : @ErrorMessage

27 |

Woopsa HTTPServer v@Version

28 |
29 | 30 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/HTTPServer/Processors/TlsProcessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net.Security; 6 | using System.Security.Cryptography.X509Certificates; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Woopsa 11 | { 12 | public class TlsProcessor : PreRouteProcessor 13 | { 14 | public TlsProcessor(string certificateLocation, string certificatePassword) 15 | { 16 | _certificate = new X509Certificate2(certificateLocation, certificatePassword); 17 | } 18 | 19 | public override Stream ProcessStream(Stream input) 20 | { 21 | SslStream secureStream = new SslStream(input, false); 22 | try 23 | { 24 | secureStream.AuthenticateAsServer(_certificate, false, System.Security.Authentication.SslProtocols.Tls, true); 25 | } 26 | catch (IOException) 27 | { 28 | return null; 29 | } 30 | return secureStream; 31 | } 32 | 33 | private X509Certificate2 _certificate; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/HTTPServer/Processors/WWWAuthenticator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace Woopsa 5 | { 6 | public abstract class BaseAuthenticator : PostRouteProcessor, IRequestProcessor 7 | { 8 | [ThreadStatic] 9 | private static string _currentUserName; 10 | 11 | /// 12 | /// This property returns the username provided to authenticate a request within the current 13 | /// webserver thread, or null if none. The value is specific to the calling thread, the property 14 | /// returns a different value in each thread. 15 | /// 16 | public static string CurrentUserName => _currentUserName; 17 | 18 | public BaseAuthenticator(string realm) 19 | { 20 | Realm = realm; 21 | } 22 | 23 | public string Realm { get;} 24 | 25 | public virtual bool Process(HTTPRequest request, HTTPResponse response) 26 | { 27 | string username; 28 | string password; 29 | bool authenticated; 30 | _currentUserName = null; 31 | if (request.Headers.ContainsKey(HTTPHeader.Authorization)) 32 | { 33 | string authString = request.Headers[HTTPHeader.Authorization].Split(' ')[1]; 34 | authString = Encoding.GetEncoding("ISO-8859-1").GetString(Convert.FromBase64String(authString)); 35 | string[] parts = authString.Split(':'); 36 | username = parts[0]; 37 | password = parts[1]; 38 | } 39 | else 40 | { 41 | username = null; 42 | password = null; 43 | } 44 | authenticated = Authenticate(request, username, password); 45 | if (authenticated) 46 | _currentUserName = username; 47 | else 48 | { 49 | response.SetHeader(HTTPHeader.WWWAuthenticate, "Basic Realm=\"" + Realm + "\""); 50 | response.WriteError(HTTPStatusCode.Unauthorized, "Unauthorized"); 51 | } 52 | return authenticated; 53 | } 54 | 55 | /// 56 | /// 57 | /// 58 | /// null if no Authorization received in HTTP headers 59 | /// null if no Authorization received in HTTP headers 60 | /// 61 | protected abstract bool Authenticate(HTTPRequest request, string username, string password); 62 | 63 | } 64 | 65 | public class AuthenticationCheckEventArgs : EventArgs 66 | { 67 | public AuthenticationCheckEventArgs(HTTPRequest request, string username, string password) 68 | { 69 | Request = request; 70 | Username = username; 71 | Password = password; 72 | } 73 | public HTTPRequest Request { get; } 74 | public string Username { get; } 75 | public string Password { get; } 76 | 77 | public bool IsAuthenticated { get; set; } 78 | } 79 | 80 | public delegate void AuthenticationCheck(object sender, AuthenticationCheckEventArgs e); 81 | 82 | public class SimpleAuthenticator : BaseAuthenticator 83 | { 84 | public SimpleAuthenticator(string realm, AuthenticationCheck authenticationCheck) : 85 | base(realm) 86 | { 87 | if (authenticationCheck != null) 88 | AuthenticationCheck = authenticationCheck; 89 | else 90 | throw new NotImplementedException("No DoCheck delegate was specified for WWWAuthenticator."); 91 | } 92 | 93 | protected override bool Authenticate(HTTPRequest request, string username, string password) 94 | { 95 | AuthenticationCheckEventArgs eventArgs = new AuthenticationCheckEventArgs(request, 96 | username, password); 97 | eventArgs.IsAuthenticated = false; 98 | AuthenticationCheck(this, eventArgs); 99 | return eventArgs.IsAuthenticated; 100 | } 101 | 102 | private AuthenticationCheck AuthenticationCheck; 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/HTTPServer/RouteHandlers/RouteHandlerFileSystem.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Woopsa 4 | { 5 | public class RouteHandlerFileSystem : IHTTPRouteHandler 6 | { 7 | private string _baseDirectory; 8 | 9 | public RouteHandlerFileSystem(string baseDirectory) 10 | { 11 | _baseDirectory = baseDirectory; 12 | } 13 | 14 | public void HandleRequest(HTTPRequest request, HTTPResponse response) 15 | { 16 | if (request.Subroute == "") 17 | { 18 | //If there really is no file requested, then we send a 404! 19 | response.WriteError(HTTPStatusCode.NotFound, "Not Found"); 20 | return; 21 | } 22 | 23 | //Find the requested file 24 | string requestedFile = _baseDirectory + request.Subroute.Replace(WoopsaConst.UrlSeparator, Path.DirectorySeparatorChar); 25 | 26 | if (!IsFileAllowed(requestedFile)) 27 | { 28 | response.WriteError(HTTPStatusCode.NotFound, "Not Found"); 29 | return; 30 | } 31 | 32 | //Does this file exist? 33 | if (File.Exists(requestedFile)) 34 | { 35 | ServeFile(requestedFile, response); 36 | } 37 | else if (Directory.Exists(requestedFile)) 38 | { 39 | //Try to find the index.html file 40 | requestedFile = requestedFile + "index.html"; 41 | if (File.Exists(requestedFile)) 42 | { 43 | ServeFile(requestedFile, response); 44 | } 45 | else 46 | { 47 | response.WriteError(HTTPStatusCode.NotFound, "Not Found"); 48 | } 49 | } 50 | else 51 | { 52 | response.WriteError(HTTPStatusCode.NotFound, "Not Found"); 53 | } 54 | } 55 | 56 | public bool AcceptSubroutes => true; 57 | 58 | /* 59 | * This is a very important function! 60 | * It tells us if a file/directory requested in 61 | * 'path' is really inside the _baseDirectory 62 | */ 63 | private bool IsFileAllowed(string path) 64 | { 65 | string realPath = Path.GetFullPath(path); 66 | string realBase = Path.GetFullPath(_baseDirectory); 67 | if (realPath.StartsWith(realBase)) 68 | { 69 | return true; 70 | } 71 | else 72 | { 73 | return false; 74 | } 75 | } 76 | 77 | private void ServeFile(string filePath, HTTPResponse response) 78 | { 79 | string extension = Path.GetExtension(filePath); 80 | response.SetHeader(HTTPHeader.ContentType, MIMETypeMap.GetMIMEType(extension)); 81 | response.SetHeader(HTTPHeader.LastModified, System.IO.File.GetLastWriteTime(filePath).ToHTTPDate()); 82 | 83 | using (var stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) 84 | response.WriteStream(stream); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/HTTPServer/RouteHandlers/RouteHandlerMemory.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Collections.Generic; 3 | using System.IO; 4 | 5 | namespace Woopsa 6 | { 7 | public class RouteHandlerMemory : IHTTPRouteHandler 8 | { 9 | public RouteHandlerMemory() 10 | { 11 | _dictionary = new Dictionary(); 12 | } 13 | 14 | public void HandleRequest(HTTPRequest request, HTTPResponse response) 15 | { 16 | if (request.Subroute == "") 17 | { 18 | //If there really is no file requested, then we send a 404! 19 | response.WriteError(HTTPStatusCode.NotFound, "Not Found"); 20 | return; 21 | } 22 | 23 | lock (_dictionary) 24 | { 25 | if (_dictionary.TryGetValue(request.Subroute, out MemoryStream resource)) 26 | { 27 | resource.Position = 0; 28 | response.WriteStream(resource); 29 | response.SetHeader("Cache-Control", "no-cache, no-store, must-revalidate"); 30 | response.SetHeader("Pragma", "no-cache"); 31 | response.SetHeader("Expires", "0"); 32 | } 33 | else 34 | response.WriteError(HTTPStatusCode.NotFound, "Not Found"); 35 | } 36 | } 37 | 38 | public void RegisterResource(string key, MemoryStream memory) 39 | { 40 | lock (_dictionary) 41 | _dictionary.Add(key, memory); 42 | } 43 | 44 | public void UpdateResource(string key, MemoryStream memory) 45 | { 46 | lock (_dictionary) 47 | _dictionary[key] = memory; 48 | } 49 | 50 | public void DeleteResource(string key) 51 | { 52 | lock (_dictionary) 53 | _dictionary.Remove(key); 54 | } 55 | 56 | public bool AcceptSubroutes => true; 57 | 58 | private IDictionary _dictionary; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/HTTPServer/RouteHandlers/RouteHandlerRedirect.cs: -------------------------------------------------------------------------------- 1 | namespace Woopsa 2 | { 3 | public enum WoopsaRedirectionType 4 | { 5 | Temporary = 1, 6 | Permanent = 2 7 | } 8 | public class RouteHandlerRedirect : IHTTPRouteHandler 9 | { 10 | public RouteHandlerRedirect(string targetLocation, WoopsaRedirectionType redirectionType) 11 | { 12 | _targetLocation = targetLocation; 13 | _redirectionType = redirectionType; 14 | } 15 | 16 | #region IHTTPRouteHandler 17 | public bool AcceptSubroutes => true; 18 | 19 | public void HandleRequest(HTTPRequest request, HTTPResponse response) 20 | { 21 | if (_redirectionType == WoopsaRedirectionType.Temporary) 22 | response.SetStatusCode((int)HTTPStatusCode.TemporaryRedirect, "Temporary redirect"); 23 | else if (_redirectionType == WoopsaRedirectionType.Permanent) 24 | response.SetStatusCode((int)HTTPStatusCode.Moved, "Permanent redirection"); 25 | response.SetHeader("Location", _targetLocation); 26 | } 27 | #endregion IHTTPRouteHandler 28 | 29 | private string _targetLocation; 30 | private WoopsaRedirectionType _redirectionType; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/HTTPServer/Routing/IHTTPRouteHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Woopsa 2 | { 3 | public interface IHTTPRouteHandler 4 | { 5 | void HandleRequest(HTTPRequest request, HTTPResponse response); 6 | bool AcceptSubroutes { get; } 7 | } 8 | 9 | public delegate void HandleRequestDelegate(HTTPRequest request, HTTPResponse response); 10 | 11 | public class RouteHandlerDelegate : IHTTPRouteHandler 12 | { 13 | public RouteHandlerDelegate(HandleRequestDelegate requestDelegate, bool acceptSubroutes = false, bool acceptWebsockets = false) 14 | { 15 | Delegate = requestDelegate; 16 | AcceptSubroutes = acceptSubroutes; 17 | } 18 | 19 | public HandleRequestDelegate Delegate { get; set; } 20 | 21 | public void HandleRequest(HTTPRequest request, HTTPResponse response) => Delegate(request, response); 22 | 23 | public bool AcceptSubroutes { get; } = false; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/HTTPServer/Routing/Processors.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Woopsa 4 | { 5 | /// 6 | /// A Post-Route Processor is a type of processing layer which 7 | /// happens just before the RouteHandler can get to it. This means 8 | /// the Route Solver has already determined which route the URL 9 | /// corresponds to. 10 | /// 11 | public abstract class PostRouteProcessor { } 12 | 13 | /// 14 | /// A Pre-Route Processor is a type of processing layer which 15 | /// happens before any route processing. This means that the 16 | /// request can still go to any other Route Handler, or that 17 | /// the HTTP request will lead to a 404, for example. It's very 18 | /// useful for making a TLS or HTTP 2 layer, for example. 19 | /// 20 | public abstract class PreRouteProcessor 21 | { 22 | public abstract Stream ProcessStream(Stream input); 23 | } 24 | 25 | /// 26 | /// A Request Processor is a processor which needs to process 27 | /// the data before a RouteHandler has generated a response 28 | /// to a request. 29 | /// 30 | /// For example, a post-route request processor could be used 31 | /// to add authentication, while a pre-route response processor could be used 32 | /// to decrypt the TLS layer or act as a very simple proxy. 33 | /// 34 | /// 35 | public interface IRequestProcessor 36 | { 37 | /// 38 | /// The function which is called automatically by the web server. 39 | /// 40 | /// The request 41 | /// The response 42 | /// 43 | /// If this function returns true, the standard routing 44 | /// mechanism continues after this call, meaning the route processing 45 | /// is transparent. If this function returns false, then 46 | /// processing is stopped short. 47 | /// 48 | bool Process(HTTPRequest request, HTTPResponse response); 49 | } 50 | 51 | /// 52 | /// A Response Processor is a processor which needs to process 53 | /// the data after a RouteHandler has generated a response 54 | /// to a request. 55 | /// 56 | /// For example, a post-route response processor could be used 57 | /// to add cookies, while a pre-route response processor could be used 58 | /// to compress the response in gzip/deflate or encrypt the result 59 | /// using the TLS layer. 60 | /// 61 | /// 62 | public interface IResponseProcessor 63 | { 64 | /// 65 | /// The function which is called automatically by the web server. 66 | /// Because this is a response processor, the user can call 67 | /// on the response 68 | /// knowing that the response has already been prepared. 69 | /// 70 | /// The request 71 | /// The response 72 | void Process(HTTPRequest request, HTTPResponse response); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/HTTPServer/Routing/RouteMapper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Woopsa 4 | { 5 | /// 6 | /// Provides mechanisms that allow to map a route to a route handler. 7 | /// This class is used mostly internally, except when adding post-route 8 | /// processors. 9 | /// 10 | public class RouteMapper 11 | { 12 | #region ctor 13 | internal RouteMapper(string route, HTTPMethod methods, IHTTPRouteHandler handler) 14 | { 15 | _route = WoopsaConst.UrlSeparator + route.Trim(WoopsaConst.UrlSeparator); 16 | _methods = methods; 17 | _handler = handler; 18 | _processors = new List(); 19 | } 20 | #endregion 21 | 22 | #region Public Members 23 | public string Route => _route; 24 | public HTTPMethod Methods => _methods; 25 | #endregion 26 | 27 | #region Private/Protected/Internal Members 28 | private string _route; 29 | private HTTPMethod _methods; 30 | private IHTTPRouteHandler _handler; 31 | internal bool AcceptSubroutes => _handler.AcceptSubroutes; 32 | 33 | private List _processors; 34 | 35 | private IEnumerable _requestProcessors 36 | { 37 | get 38 | { 39 | foreach (PostRouteProcessor processor in _processors) 40 | { 41 | if (processor is IRequestProcessor) 42 | { 43 | yield return processor as IRequestProcessor; 44 | } 45 | } 46 | } 47 | } 48 | 49 | private IEnumerable _responseProcessors 50 | { 51 | get 52 | { 53 | foreach (PostRouteProcessor processor in _processors) 54 | { 55 | if (processor is IResponseProcessor) 56 | { 57 | yield return processor as IResponseProcessor; 58 | } 59 | } 60 | } 61 | } 62 | #endregion 63 | 64 | #region Public methods 65 | public void AddProcessor(PostRouteProcessor processor) 66 | { 67 | _processors.Add(processor); 68 | } 69 | 70 | public bool RemoveProcessor(PostRouteProcessor processor) 71 | { 72 | return _processors.Remove(processor); 73 | } 74 | #endregion 75 | 76 | #region Private/Protected/Internal methods 77 | internal void HandleRequest(HTTPRequest request, HTTPResponse response) 78 | { 79 | foreach (IRequestProcessor processor in _requestProcessors) 80 | { 81 | if (!processor.Process(request, response)) 82 | { 83 | return; 84 | } 85 | } 86 | _handler.HandleRequest(request, response); 87 | foreach(IResponseProcessor processor in _responseProcessors) 88 | { 89 | processor.Process(request, response); 90 | } 91 | } 92 | #endregion 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/HTTPServer/Utils/Exceptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Woopsa 4 | { 5 | [Serializable] 6 | public class HTTPException : InvalidOperationException { } 7 | 8 | [Serializable] 9 | public class HeadersAlreadySentException : InvalidOperationException { } 10 | 11 | public enum RoutingErrorType 12 | { 13 | MULTIPLE_MATCHES, //If the RouteSolver finds multiple matches for a request, this error is raised for every match (except the first one) 14 | NO_MATCHES, //The RouteSolver sends a 404 in this case, but the event is raised for debugging purposes 15 | INTERNAL //All other routing error types 16 | } 17 | 18 | public class RoutingErrorEventArgs : EventArgs 19 | { 20 | public string Error { get; } 21 | public HTTPRequest Request { get; } 22 | public RoutingErrorType Type { get; } 23 | 24 | public RoutingErrorEventArgs(RoutingErrorType type, string error, HTTPRequest request) 25 | { 26 | Type = type; 27 | Error = error; 28 | Request = request; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/HTTPServer/Utils/HTTPRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.Specialized; 3 | 4 | namespace Woopsa 5 | { 6 | /// 7 | /// Provides a mechanism to get information about an HTTP Request made by a client. 8 | /// 9 | /// This class is created internally by the Web Server and thus cannot be created from 10 | /// outside code. To handle requests and response, the 11 | /// interface or the delegate methods in which is available from the 12 | /// property. 13 | /// 14 | /// 15 | public class HTTPRequest 16 | { 17 | #region ctor 18 | internal HTTPRequest(HTTPMethod method, string fullURL) 19 | { 20 | FullURL = fullURL; 21 | BaseURL = FullURL; 22 | Method = method; 23 | _headers = new Dictionary(); 24 | } 25 | #endregion 26 | 27 | #region Public Members 28 | /// 29 | /// Provides all the HTTP Headers that were sent by the client in a key/value list. 30 | /// 31 | public ReadOnlyHeaderDictionary Headers 32 | { 33 | get 34 | { 35 | if (_readOnlyHeaders == null) 36 | { 37 | _readOnlyHeaders = new ReadOnlyHeaderDictionary(_headers); 38 | } 39 | return _readOnlyHeaders; 40 | } 41 | } 42 | 43 | /// 44 | /// Provides the Full URL that was sent by the client. This URL thus contains 45 | /// the query parameters (values preceded by the ? sign in a URL) as well as the 46 | /// Subroute when Subrouting is allowed by a 47 | /// 48 | public string FullURL { get; internal set; } 49 | 50 | /// 51 | /// Provides the Base URL that was sent by the client. This URL is thus stripped 52 | /// of the query parameters (values preceded by the ? sign in a URL) but still contains 53 | /// the entire Subroute when Subrouting is allowed by a 54 | /// 55 | public string BaseURL { get; internal set; } 56 | 57 | /// 58 | /// When Subrouting is allowed by a , this 59 | /// property returns the Subroute. For example, if the RouteSolver is configured to accept 60 | /// Subroutes on the /public url, and a request is made for /public/files/hello_world.html, then 61 | /// this property will thus be /files/hello_world.html 62 | /// 63 | public string Subroute { get; internal set; } 64 | 65 | /// 66 | /// Provides the HTTP Method that was requested by the client (GET, POST, PUT, etc.) 67 | /// 68 | public HTTPMethod Method { get; internal set; } 69 | 70 | /// 71 | /// Provides the HTTP Method that was requested by the client, as a string (GET, POST, PUT, etc.) 72 | /// 73 | public string MethodAsString 74 | { 75 | get 76 | { 77 | switch (Method) 78 | { 79 | case HTTPMethod.GET: 80 | return "GET"; 81 | case HTTPMethod.POST: 82 | return "POST"; 83 | case HTTPMethod.HEAD: 84 | return "HEAD"; 85 | } 86 | return null; 87 | } 88 | } 89 | 90 | /// 91 | /// Provides all the variables that were sent by the client in the Query String, which is 92 | /// part of the URL (this is the part of the URL that follows the question mark (?) sign) 93 | /// 94 | public NameValueCollection Query { get; internal set; } 95 | 96 | /// 97 | /// Provides all the variables that were sent by the client in the POST request. Note: 98 | /// only POST requests encoded with xwwww-form-urlencoded method are supported. File uploads, 99 | /// which are generally encoded with multipart/form-data, are not supported. 100 | /// 101 | public NameValueCollection Body { get; internal set; } 102 | #endregion 103 | 104 | #region Private/Protected/Internal Members 105 | private ReadOnlyHeaderDictionary _readOnlyHeaders = null; 106 | internal Dictionary _headers; 107 | #endregion 108 | } 109 | 110 | public class ReadOnlyHeaderDictionary 111 | { 112 | public ReadOnlyHeaderDictionary(Dictionary headers) 113 | { 114 | _headers = headers; 115 | } 116 | 117 | public string this[string key] => _headers[key.ToLower()]; 118 | 119 | public bool ContainsKey(string key) => _headers.ContainsKey(key.ToLower()); 120 | 121 | private readonly Dictionary _headers; 122 | } 123 | } -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/Services/MultiRequest/WoopsaMultiRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text.Json; 5 | 6 | namespace Woopsa 7 | { 8 | public static class WoopsaMultiRequestConst 9 | { 10 | public const string WoopsaMultiRequestMethodName = "MultiRequest"; 11 | public const string WoopsaMultiRequestArgumentName = "Requests"; 12 | } 13 | 14 | public class WoopsaMultiRequestHandler 15 | { 16 | public WoopsaMultiRequestHandler(WoopsaObject root, WoopsaServer server) 17 | { 18 | _server = server; 19 | 20 | new WoopsaMethod(root, 21 | WoopsaMultiRequestConst.WoopsaMultiRequestMethodName, 22 | WoopsaValueType.JsonData, 23 | new List { new WoopsaMethodArgumentInfo(WoopsaMultiRequestConst.WoopsaMultiRequestArgumentName, WoopsaValueType.JsonData) }, 24 | (s) => (HandleCall(s.ElementAt(0))) 25 | ); 26 | } 27 | 28 | private WoopsaValue HandleCall(IWoopsaValue requestsArgument) 29 | { 30 | using (new WoopsaServerModelAccessFreeSection(_server)) 31 | { 32 | ServerRequest[] requestsList = JsonSerializer.Deserialize(requestsArgument.AsText, WoopsaUtils.ObjectToInferredTypesConverterOptions); 33 | List responses = new List(); 34 | foreach (var request in requestsList) 35 | { 36 | string result = null; 37 | try 38 | { 39 | using (new WoopsaServerModelAccessLockedSection(_server)) 40 | { 41 | if (request.Verb.Equals(WoopsaFormat.VerbRead)) 42 | result = _server.ReadValue(request.Path); 43 | else if (request.Verb.Equals(WoopsaFormat.VerbMeta)) 44 | result = _server.GetMetadata(request.Path); 45 | else if (request.Verb.Equals(WoopsaFormat.VerbWrite)) 46 | result = _server.WriteValueDeserializedJson(request.Path, request.Value); 47 | else if (request.Verb.Equals(WoopsaFormat.VerbInvoke)) 48 | result = _server.InvokeMethodDeserializedJson(request.Path, 49 | request.Arguments); 50 | } 51 | } 52 | catch (Exception e) 53 | { 54 | result = WoopsaFormat.Serialize(e); 55 | } 56 | MultipleRequestResponse response = new MultipleRequestResponse(); 57 | response.Id = request.Id; 58 | response.Result = result; 59 | responses.Add(response); 60 | } 61 | return WoopsaValue.CreateUnchecked(responses.Serialize(), WoopsaValueType.JsonData); 62 | } 63 | } 64 | 65 | private WoopsaServer _server; 66 | } 67 | 68 | public class MultipleRequestResponse 69 | { 70 | public int Id { get; set; } 71 | public string Result { get; set; } 72 | } 73 | 74 | public class ServerRequest 75 | { 76 | public int Id { get; set; } 77 | 78 | public string Verb { get; set; } 79 | 80 | public string Path { get; set; } 81 | 82 | public object Value { get; set; } 83 | 84 | public Dictionary Arguments { get; set; } 85 | 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/Services/SubscriptionService/WoopsaServerNotification.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Woopsa 4 | { 5 | public class WoopsaServerNotification : IWoopsaNotification 6 | { 7 | public WoopsaServerNotification(IWoopsaValue value, int subscriptionId) 8 | { 9 | Value = value; 10 | SubscriptionId = subscriptionId; 11 | } 12 | 13 | public IWoopsaValue Value { get; } 14 | 15 | public int SubscriptionId { get; } 16 | 17 | public int Id { get; set; } 18 | } 19 | 20 | public class WoopsaServerNotifications : IWoopsaNotifications 21 | { 22 | public WoopsaServerNotifications() 23 | { 24 | _notifications = new List(); 25 | } 26 | 27 | public void Add(IWoopsaNotification notification) 28 | { 29 | _notifications.Add(notification); 30 | } 31 | 32 | public void AddRange(IEnumerable notification) 33 | { 34 | _notifications.AddRange(notification); 35 | } 36 | 37 | public IEnumerable Notifications => _notifications; 38 | 39 | private readonly List _notifications; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/Utils/WoopsaTypeUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Woopsa 5 | { 6 | public static class WoopsaTypeUtils 7 | { 8 | /// 9 | /// Determines the WoopsaValueType based on a .NET type. 10 | /// 11 | /// The .NET type to try to get the WoopsaValueType from 12 | /// The inferred WoopsaValueType. If the type cannot be inferred, this value will be WoopsaValueType.Null 13 | /// true if the type could be inferred, false otherwise. A return value of false will also result in WoopsaValueType.Null 14 | public static bool InferWoopsaType(Type targetType, out WoopsaValueType resultType) 15 | { 16 | if (targetType == typeof(void)) 17 | { 18 | resultType = WoopsaValueType.Null; 19 | return true; 20 | } 21 | else if (targetType.IsEnum) 22 | { 23 | resultType = WoopsaValueType.Text; 24 | return true; 25 | } 26 | else 27 | switch (System.Type.GetTypeCode(targetType)) 28 | { 29 | case TypeCode.Boolean: 30 | resultType = WoopsaValueType.Logical; 31 | return true; 32 | case TypeCode.Byte: 33 | case TypeCode.SByte: 34 | case TypeCode.UInt16: 35 | case TypeCode.UInt32: 36 | case TypeCode.UInt64: 37 | case TypeCode.Int16: 38 | case TypeCode.Int32: 39 | case TypeCode.Int64: 40 | resultType = WoopsaValueType.Integer; 41 | return true; 42 | case TypeCode.Decimal: 43 | case TypeCode.Double: 44 | case TypeCode.Single: 45 | resultType = WoopsaValueType.Real; 46 | return true; 47 | case TypeCode.DateTime: 48 | resultType = WoopsaValueType.DateTime; 49 | return true; 50 | case TypeCode.String: 51 | resultType = WoopsaValueType.Text; 52 | return true; 53 | default: 54 | if (targetType == typeof(TimeSpan)) 55 | { 56 | resultType = WoopsaValueType.TimeSpan; 57 | return true; 58 | } 59 | else 60 | { 61 | resultType = WoopsaValueType.Null; 62 | return false; 63 | } 64 | } 65 | } 66 | 67 | /// 68 | /// Determines if the .net type matches a WoopsaValueType 69 | /// Some .net reference types (like string) are WoopsaValueType 70 | /// 71 | /// The .NET type to test 72 | /// true if the type matches a woopsa value type 73 | public static bool IsWoopsaValueType(Type type) => InferWoopsaType(type, out _); 74 | 75 | /// 76 | /// Determines if the .net type matches a Woopsa reference type (object) 77 | /// Some value types of .net have no corresponding Woopsa type, and are not reference type. 78 | /// 79 | /// The .NET type to test 80 | /// true if the type matches a woopsa reference type 81 | public static bool IsWoopsaReferenceType(Type type) => !IsWoopsaValueType(type) && !type.IsValueType; 82 | 83 | public static bool GetGenericEnumerableItemType(Type type, out Type itemsType) 84 | { 85 | if (type.IsInterface) 86 | { 87 | if (type.IsGenericType && 88 | type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) 89 | { 90 | itemsType = type.GetGenericArguments()[0]; 91 | return true; 92 | } 93 | } 94 | else 95 | foreach (Type interfaceType in type.GetInterfaces()) 96 | if (GetGenericEnumerableItemType(interfaceType, out itemsType)) 97 | return true; 98 | itemsType = null; 99 | return false; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Sources/DotNet/Woopsa/Woopsa.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | Woopsa is a protocol that's simple, lightweight, free, open-source, web and object-oriented, publish-subscribe, real-time capable and Industry 4.0 ready. It contributes to the revolution of the Internet of Things 7 | Objectis SA 8 | Woopsa 9 | http://www.woopsa.org/ 10 | https://github.com/woopsa-protocol/Woopsa 11 | git 12 | Network Open Protocol C# Mono Json Client Server IoT Industry4.0 13 | MIT 14 | 2.0.0 15 | $(VersionSuffix) 16 | 1.3.2 17 | 18 | 8.0 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | <_Parameter1>$(MSBuildProjectName)Test 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaDemoClient/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Woopsa; 5 | 6 | namespace WoopsaDemoClient 7 | { 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | Console.WriteLine(" *** Welcome to the Woopsa Demo Client! *** "); 13 | Console.WriteLine(" Note: read the source code to understand what's happening behind the scenes!"); 14 | Console.WriteLine(""); 15 | 16 | string serverUrl; 17 | 18 | if (File.Exists("url.config")) 19 | { 20 | serverUrl = File.ReadAllText("url.config"); 21 | Console.WriteLine("Using url.config"); 22 | } 23 | else 24 | { 25 | Console.Write("Please enter the Woopsa server URL or leave blank for default (http://localhost/woopsa): "); 26 | serverUrl = Console.ReadLine(); 27 | if (serverUrl == "") 28 | serverUrl = "http://localhost/woopsa"; 29 | 30 | } 31 | 32 | WoopsaClient client = new WoopsaClient(serverUrl); 33 | 34 | Console.WriteLine("Woopsa client created on URL: {0}", serverUrl); 35 | 36 | WoopsaBoundClientObject root = client.CreateBoundRoot(); 37 | ExploreItem(root); 38 | 39 | // Leave the DOS window open 40 | Console.WriteLine("Press any key to exit..."); 41 | Console.ReadLine(); 42 | client.Dispose(); 43 | } 44 | 45 | static void ExploreItem(IWoopsaObject obj, int indent = 0) 46 | { 47 | // Display all properties and their values 48 | string indentString = ""; 49 | for (int i = 0; i < indent; i++) 50 | indentString += " "; 51 | Console.WriteLine(indentString + "{0}", obj.Name); 52 | Console.WriteLine(indentString + "Properties and their values:"); 53 | foreach (WoopsaClientProperty property in obj.Properties) 54 | { 55 | Console.WriteLine(indentString + " * {0} : {1} = {2}", property.Name, property.Type, property.Value); 56 | if (property.Name == "Altitude") 57 | { 58 | // Create a subscription for example's sake 59 | Console.WriteLine(indentString + " => Creating a subscription"); 60 | property.Subscribe(property_Change); 61 | 62 | // Actually change the value 63 | Console.WriteLine(indentString + " => Changing value to 1"); 64 | property.Value = new WoopsaValue(1); 65 | } 66 | } 67 | 68 | // Display methods and their arguments 69 | Console.WriteLine(indentString + "Methods and their arguments:"); 70 | foreach (WoopsaMethod method in obj.Methods) 71 | { 72 | // Display the method 73 | Console.WriteLine(indentString + " * {0} : {1}", method.Name, method.ReturnType); 74 | foreach (WoopsaMethodArgumentInfo argumentInfo in method.ArgumentInfos) 75 | { 76 | Console.WriteLine(indentString + " * {0} : {1}", argumentInfo.Name, argumentInfo.Type); 77 | } 78 | 79 | // As an example, if we find a SayHello method (like in the demo server), 80 | // we call it. That way you can see how to call methods using the standard 81 | // client! 82 | if (method.Name == "GetWeatherAtDate") 83 | { 84 | Console.WriteLine(indentString + " => GetWeatherAtDate found! Calling it now..."); 85 | Console.WriteLine(indentString + " => Result = {0}", method.Invoke(new List() 86 | { 87 | new WoopsaValue(DateTime.Now) 88 | }) 89 | ); 90 | } 91 | } 92 | 93 | Console.WriteLine(indentString + "Items:"); 94 | foreach (WoopsaBoundClientObject item in obj.Items) 95 | { 96 | ExploreItem(item, indent+1); 97 | } 98 | } 99 | 100 | static void property_Change(object sender, WoopsaNotificationEventArgs e) 101 | { 102 | // TODO : type de sender incorrect 103 | Console.WriteLine("Property {0} has changed, new value = {1}", (sender as WoopsaClientProperty).Name, e.Notification.Value); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaDemoClient/WoopsaDemoClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaDemoServer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Sockets; 4 | using Woopsa; 5 | 6 | namespace WoopsaDemoServer 7 | { 8 | public class Thermostat 9 | { 10 | public double SetPoint { get; set; } 11 | 12 | public Thermostat() 13 | { 14 | SetPoint = 20; 15 | } 16 | } 17 | 18 | [WoopsaVisibilityAttribute(WoopsaVisibility.DefaultIsVisible)] 19 | public class WeatherStation 20 | { 21 | public double Temperature { get; } 22 | 23 | public bool IsRaining { get; set; } 24 | 25 | public int Altitude { get; set; } 26 | 27 | public double Sensitivity { get; set; } 28 | 29 | public string City { get; set; } 30 | 31 | public DateTime Time => DateTime.Now; 32 | 33 | public TimeSpan TimeSinceLastRain { get; set; } 34 | 35 | public Thermostat Thermostat { get; } 36 | 37 | public WeatherStation() 38 | { 39 | Temperature = 24.2; 40 | IsRaining = false; 41 | Altitude = 430; 42 | Sensitivity = 0.5; 43 | City = "Geneva"; 44 | TimeSinceLastRain = TimeSpan.FromDays(3); 45 | Thermostat = new Thermostat(); 46 | } 47 | 48 | public string GetWeatherAtDate(DateTime date) 49 | { 50 | switch (date.DayOfWeek) 51 | { 52 | case DayOfWeek.Monday: 53 | return "rainy"; 54 | default: 55 | return "sunny"; 56 | } 57 | } 58 | } 59 | 60 | 61 | class Program 62 | { 63 | static void Main(string[] args) 64 | { 65 | try 66 | { 67 | WeatherStation root = new WeatherStation(); 68 | bool done = false; 69 | using (WoopsaServer woopsaServer = new WoopsaServer(root, 80)) 70 | { 71 | 72 | Console.WriteLine("Woopsa server listening on http://localhost:{0}{1}", woopsaServer.WebServer.Port, woopsaServer.RoutePrefix); 73 | Console.WriteLine("Some examples of what you can do directly from your browser:"); 74 | Console.WriteLine(" * View the object hierarchy of the root object:"); 75 | Console.WriteLine(" http://localhost:{0}{1}meta/", woopsaServer.WebServer.Port, woopsaServer.RoutePrefix); 76 | Console.WriteLine(" * Read the value of a property:"); 77 | Console.WriteLine(" http://localhost:{0}{1}read/Temperature", woopsaServer.WebServer.Port, woopsaServer.RoutePrefix); 78 | 79 | 80 | Console.WriteLine(); 81 | Console.WriteLine("Commands : QUIT, AUTH, NOAUTH"); 82 | do 83 | { 84 | Console.Write(">"); 85 | switch (Console.ReadLine().ToUpper()) 86 | { 87 | case "QUIT": 88 | done = true; 89 | break; 90 | case "AUTH": 91 | woopsaServer.Authenticator = new SimpleAuthenticator( 92 | "WoopsaDemoServer", 93 | (sender, e) => { e.IsAuthenticated = e.Username == "woopsa"; }); 94 | break; 95 | case "NOAUTH": 96 | woopsaServer.Authenticator = null; 97 | break; 98 | default: 99 | Console.WriteLine("Invalid command"); 100 | break; 101 | } 102 | } 103 | while (!done); 104 | } 105 | } 106 | catch (SocketException e) 107 | { 108 | // A SocketException is caused by an application already listening on a port in most cases 109 | // Applications known to use port 80: 110 | // - On Windows 10, IIS is on by default on some configurations. Disable it here: 111 | // http://stackoverflow.com/questions/30758894/apache-server-xampp-doesnt-run-on-windows-10-port-80 112 | // - IIS 113 | // - Apache 114 | // - Nginx 115 | // - Skype 116 | Console.WriteLine("Error: Could not start Woopsa Server. Most likely because an application is already listening on port 80."); 117 | Console.WriteLine("Known culprits:"); 118 | Console.WriteLine(" - On Windows 10, IIS is on by default on some configurations."); 119 | Console.WriteLine(" - Skype"); 120 | Console.WriteLine(" - Apache, nginx, etc."); 121 | Console.WriteLine("SocketException: {0}", e.Message); 122 | Console.ReadLine(); 123 | } 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaDemoServer/WoopsaDemoServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaTest/TestResources/EmbeddedResource/Class1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EmbeddedResource 8 | { 9 | public class Class1 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaTest/TestResources/EmbeddedResource/EmbeddedResource.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 8.0 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaTest/TestResources/EmbeddedResource/Images/SubImages/woopsa-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woopsa-protocol/Woopsa/589b8cac3b70aab9cc07a85ca1d5414c0361f682/Sources/DotNet/WoopsaTest/TestResources/EmbeddedResource/Images/SubImages/woopsa-logo.png -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaTest/TestResources/EmbeddedResource/Images/woopsa-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woopsa-protocol/Woopsa/589b8cac3b70aab9cc07a85ca1d5414c0361f682/Sources/DotNet/WoopsaTest/TestResources/EmbeddedResource/Images/woopsa-logo.png -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaTest/TestResources/Woopsa.EmbeddedResource/Class1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Woopsa.EmbeddedResource 8 | { 9 | public class Class1 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaTest/TestResources/Woopsa.EmbeddedResource/Images/SubImages/woopsa-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woopsa-protocol/Woopsa/589b8cac3b70aab9cc07a85ca1d5414c0361f682/Sources/DotNet/WoopsaTest/TestResources/Woopsa.EmbeddedResource/Images/SubImages/woopsa-logo.png -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaTest/TestResources/Woopsa.EmbeddedResource/Images/woopsa-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woopsa-protocol/Woopsa/589b8cac3b70aab9cc07a85ca1d5414c0361f682/Sources/DotNet/WoopsaTest/TestResources/Woopsa.EmbeddedResource/Images/woopsa-logo.png -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaTest/TestResources/Woopsa.EmbeddedResource/Woopsa.EmbeddedResource.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 8.0 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaTest/UnitTestClasses.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Woopsa; 7 | 8 | namespace WoopsaTest 9 | { 10 | public class TestObjectServer 11 | { 12 | public int Votes { get; set; } 13 | 14 | public string StringValue { get; set; } 15 | 16 | public void IncrementVotes(int count) 17 | { 18 | Votes += count; 19 | } 20 | } 21 | 22 | public class TestObjectServerAuthentification 23 | { 24 | public int Votes { get; set; } 25 | 26 | public void IncrementVotes(int count) 27 | { 28 | Votes += count; 29 | } 30 | 31 | public string CurrentUserName => BaseAuthenticator.CurrentUserName; 32 | } 33 | 34 | public interface InterfaceA 35 | { 36 | string A { get; set; } 37 | 38 | void MethodA(); 39 | } 40 | 41 | public interface InterfaceB : InterfaceA 42 | { 43 | int B { get; set; } 44 | 45 | void MethodB(); 46 | } 47 | 48 | public class ClassInterfaceA : InterfaceA 49 | { 50 | public string A { get; set; } 51 | 52 | public void MethodA() { } 53 | } 54 | 55 | public class ClassInterfaceB : InterfaceB 56 | { 57 | public string A { get; set; } 58 | public int B { get; set; } 59 | 60 | public void MethodA() { } 61 | public void MethodB() { } 62 | } 63 | 64 | public enum Day { Monday, Thursday, Wednesday } 65 | 66 | public class ClassAInner1Inner 67 | { 68 | public string APropertyString { get; set; } 69 | 70 | [WoopsaVisible(true)] 71 | public string APropertyVisible { get; set; } 72 | 73 | [WoopsaVisible(false)] 74 | public string APropertyHidden { get; set; } 75 | 76 | } 77 | 78 | public class ClassAInner1 79 | { 80 | public ClassAInner1() 81 | { 82 | Inner = new ClassAInner1Inner(); 83 | } 84 | 85 | public int APropertyInt { get; set; } 86 | 87 | [WoopsaVisible(false)] 88 | public int APropertyIntHidden { get; set; } 89 | 90 | [WoopsaVisible(true)] 91 | public int APropertyIntVisible { get; set; } 92 | 93 | [WoopsaVisible(true)] 94 | public ClassAInner1Inner Inner { get; set; } 95 | } 96 | 97 | public class SubClassAInner1 : ClassAInner1 98 | { 99 | [WoopsaVisible(true)] 100 | public int ExtraProperty { get; set; } 101 | } 102 | 103 | [WoopsaVisibility(WoopsaVisibility.DefaultIsVisible | WoopsaVisibility.Inherited | WoopsaVisibility.ObjectClassMembers)] 104 | public class SubClassAInner2 : SubClassAInner1 105 | { 106 | } 107 | 108 | public class ClassA 109 | { 110 | public ClassA() 111 | { 112 | Inner1 = new ClassAInner1(); 113 | } 114 | public bool APropertyBool { get; set; } 115 | 116 | [WoopsaVisible(false)] 117 | public DateTime APropertyDateTime { get; set; } 118 | public DateTime APropertyDateTime2 { get; set; } 119 | 120 | [WoopsaVisible(true)] 121 | public ClassAInner1 Inner1 { get; set; } 122 | 123 | public Day Day { get; set; } 124 | } 125 | 126 | [WoopsaVisibility(WoopsaVisibility.DefaultIsVisible | WoopsaVisibility.Inherited | WoopsaVisibility.MethodSpecialName)] 127 | public class ClassB : ClassA 128 | { 129 | public int APropertyInt { get; set; } 130 | 131 | double APropertyDouble { get; set; } 132 | } 133 | 134 | [WoopsaVisibility(WoopsaVisibility.None)] 135 | public class ClassC : ClassB 136 | { 137 | [WoopsaVisible] 138 | [WoopsaValueType(WoopsaValueType.Integer)] 139 | public string APropertyText { get; set; } 140 | 141 | [WoopsaVisible] 142 | public TimeSpan APropertyTimeSpan { get; set; } 143 | 144 | // This one must not be published (private) 145 | [WoopsaVisible] 146 | private double APropertyDouble2 { get; set; } 147 | 148 | [WoopsaVisible] 149 | [WoopsaValueType(WoopsaValueType.JsonData)] 150 | public string APropertyJson { get; set; } 151 | } 152 | 153 | public class ClassD 154 | { 155 | public ClassD(int n) { APropertyInt = n; } 156 | public int APropertyInt { get; set; } 157 | } 158 | 159 | public class ClassE 160 | { 161 | public ClassE() 162 | { 163 | } 164 | 165 | public Day Day { get; set; } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaTest/UnitTestHttpServer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System.Net.Http; 3 | using Woopsa; 4 | 5 | namespace WoopsaTest 6 | { 7 | [TestClass] 8 | public class UnitTestHttpServer 9 | { 10 | #region Consts 11 | 12 | public const int TestingPort = 9999; 13 | public static string TestingUrl => $"http://localhost:{TestingPort}/resources"; 14 | 15 | #endregion 16 | 17 | [TestMethod] 18 | public void TestRouteHandlerEmbeddedResource() 19 | { 20 | WoopsaRoot serverRoot = new WoopsaRoot(); 21 | TestObjectServer objectServer = new TestObjectServer(); 22 | WoopsaObjectAdapter adapter = new WoopsaObjectAdapter(serverRoot, "TestObject", objectServer); 23 | using (WoopsaServer server = new WoopsaServer(serverRoot, TestingPort)) 24 | { 25 | var test1 = new EmbeddedResource.Class1(); // Force to load assembly 26 | var test2 = new Woopsa.EmbeddedResource.Class1(); // Force to load assembly 27 | var routeHandlerEmbeddedResources = new RouteHandlerEmbeddedResources(); 28 | server.WebServer.Routes.Add("resources", HTTPMethod.GET, 29 | routeHandlerEmbeddedResources); 30 | using (HttpClient client = new HttpClient()) 31 | { 32 | var request = client.GetAsync($"{TestingUrl}/EmbeddedResource/Images/woopsa-logo.png"); 33 | request.Wait(); 34 | Assert.IsTrue(request.Result.IsSuccessStatusCode); 35 | request = client.GetAsync($"{TestingUrl}/EmbeddedResource/Images/SubImages/woopsa-logo.png"); 36 | request.Wait(); 37 | Assert.IsTrue(request.Result.IsSuccessStatusCode); 38 | 39 | request = client.GetAsync($"{TestingUrl}/Woopsa.EmbeddedResource/Images/woopsa-logo.png"); 40 | request.Wait(); 41 | Assert.IsTrue(request.Result.IsSuccessStatusCode); 42 | 43 | request = client.GetAsync($"{TestingUrl}/Woopsa.EmbeddedResource/Images/SubImages/woopsa-logo.png"); 44 | request.Wait(); 45 | Assert.IsTrue(request.Result.IsSuccessStatusCode); 46 | } 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaTest/UnitTestReflection.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Woopsa; 8 | 9 | namespace WoopsaTest 10 | { 11 | [TestClass] 12 | public class UnitTestReflection 13 | { 14 | 15 | [TestMethod] 16 | public void TestWoopsaReflectionIsWoopsaType() 17 | { 18 | Assert.IsTrue(WoopsaReflection.IsWoopsaValueType(null, typeof(bool))); 19 | Assert.IsTrue(WoopsaReflection.IsWoopsaValueType(null, typeof(int))); 20 | Assert.IsTrue(WoopsaReflection.IsWoopsaValueType(null, typeof(double))); 21 | Assert.IsTrue(WoopsaReflection.IsWoopsaValueType(null, typeof(DateTime))); 22 | Assert.IsTrue(WoopsaReflection.IsWoopsaValueType(null, typeof(TimeSpan))); 23 | Assert.IsTrue(WoopsaReflection.IsWoopsaValueType(null, typeof(string))); 24 | Assert.IsTrue(WoopsaReflection.IsWoopsaValueType(null, typeof(TypeCode))); 25 | Assert.IsFalse(WoopsaReflection.IsWoopsaValueType(null, typeof(List))); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaTest/UnitTestWoopsaConverters.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Woopsa; 4 | 5 | namespace WoopsaTest 6 | { 7 | [TestClass] 8 | public class UnitTestWoopsaConverters 9 | { 10 | [TestMethod] 11 | public void TestWoopsaConverters() 12 | { 13 | var converters = new WoopsaConverters(); 14 | converters.RegisterConverter(typeof(decimal), WoopsaConverterDecimal.Converter, WoopsaValueType.Real); 15 | var resut = converters.InferWoopsaType(typeof(decimal), out var valueType, out var converter); 16 | Assert.IsTrue(resut); 17 | Assert.IsNotNull(converter); 18 | Assert.AreEqual(WoopsaValueType.Real, valueType); 19 | Assert.AreEqual(typeof(WoopsaConverterDecimal), converter.GetType()); 20 | } 21 | } 22 | 23 | class WoopsaConverterDecimal : WoopsaConverterDefault 24 | { 25 | static WoopsaConverterDecimal() 26 | { 27 | Converter = new WoopsaConverterDecimal(); 28 | } 29 | 30 | public static readonly WoopsaConverterDecimal Converter; 31 | 32 | public override object FromWoopsaValue(IWoopsaValue value, Type targetType) 33 | { 34 | if (targetType == typeof(decimal)) 35 | return decimal.Parse(value.AsText); 36 | else 37 | return base.FromWoopsaValue(value, targetType); 38 | } 39 | 40 | public override object GetDefaultValue(Type type) 41 | { 42 | return 0M; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaTest/UnitTestWoopsaNotification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using Microsoft.VisualStudio.TestTools.UnitTesting; 8 | using Woopsa; 9 | using System.Diagnostics; 10 | 11 | namespace WoopsaTest 12 | { 13 | [TestClass] 14 | public class UnitTestWoopsaNotification 15 | { 16 | #region Consts 17 | 18 | public const int TestingPort = 9999; 19 | public static string TestingUrl => $"http://localhost:{TestingPort}/woopsa"; 20 | 21 | #endregion 22 | 23 | [TestMethod] 24 | public void TestWoopsaWaitNotification() 25 | { 26 | TestObjectServer objectServer = new TestObjectServer(); 27 | using (WoopsaServer server = new WoopsaServer(objectServer, TestingPort)) 28 | { 29 | using (WoopsaClient client = new WoopsaClient(TestingUrl)) 30 | { 31 | WoopsaBoundClientObject root = client.CreateBoundRoot(); 32 | // Just to show how to see all items 33 | foreach (var item in root.Items) 34 | { 35 | Console.WriteLine("Item = " + item.Name); 36 | if (item.Name == "SubscriptionService") 37 | Console.WriteLine("Trouvé"); 38 | } 39 | 40 | // create a subscription object 41 | WoopsaObject subscription = root.Items.ByNameOrNull("SubscriptionService") as WoopsaObject; 42 | if (subscription != null) 43 | { 44 | int result = 0; 45 | WoopsaMethod methodCreateScubscriptionChannel = subscription.Methods.ByNameOrNull("CreateSubscriptionChannel"); 46 | if (methodCreateScubscriptionChannel != null) 47 | // call the method "CreateSubscriptionChannel" on the server 48 | result = methodCreateScubscriptionChannel.Invoke(1000); // define the queue size 49 | int channel = result; 50 | 51 | WoopsaMethod methodRegisterScubscription = subscription.Methods.ByNameOrNull("RegisterSubscription"); 52 | if (methodRegisterScubscription != null) 53 | // call the method "registerScubscription" on the server 54 | result = methodRegisterScubscription.Invoke(channel, WoopsaValue.WoopsaRelativeLink("/Votes"), 0.01, 0.01); 55 | int subscriptionNbr = result; 56 | 57 | WoopsaJsonData jData; 58 | WoopsaMethod methodWaitNotification = subscription.Methods.ByNameOrNull("WaitNotification"); 59 | if (methodWaitNotification != null) 60 | { 61 | Stopwatch watch = new Stopwatch(); 62 | watch.Start(); 63 | // call the method "WaitNotification" on the server 64 | Thread.Sleep(100); 65 | jData = methodWaitNotification.Invoke(channel, 0).JsonData; 66 | Assert.IsTrue(jData.Length > 0); 67 | int lastNotification; 68 | lastNotification = jData[0]["Id"]; 69 | Assert.AreEqual(lastNotification, 1); 70 | // Get notifications again 71 | Thread.Sleep(100); 72 | jData = methodWaitNotification.Invoke(channel, 0).JsonData; 73 | Assert.IsTrue(jData.Length > 0); 74 | lastNotification = jData[0]["Id"]; 75 | Assert.AreEqual(lastNotification, 1); 76 | } 77 | } 78 | } 79 | 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaTest/UnitTestWoopsaObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Woopsa; 4 | using System.Linq; 5 | using System.Diagnostics; 6 | 7 | namespace WoopsaTest 8 | { 9 | [TestClass] 10 | public class UnitTestWoopsaObject 11 | { 12 | [TestMethod] 13 | public void TestWoopsaObjects() 14 | { 15 | WoopsaRoot root = new WoopsaRoot(); 16 | WoopsaObject tunnel1 = new WoopsaObject(root, "Tunnel1"); 17 | 18 | Assert.AreEqual(root.Items.Count(), 1); 19 | WoopsaObject tunnel2 = new WoopsaObject(root, "Tunnel2"); 20 | Assert.AreEqual(root.Items.Count(), 2); 21 | WoopsaObject coMessung1 = new WoopsaObject(tunnel1, "CoMessung1"); 22 | Assert.AreEqual(coMessung1.GetPath(), "/Tunnel1/CoMessung1"); 23 | 24 | WoopsaProperty property1 = new WoopsaProperty(coMessung1, "Level", WoopsaValueType.Real, 25 | (sender) => 1040.0); 26 | int property2Value = 0; 27 | WoopsaProperty property2 = new WoopsaProperty(coMessung1, "Variation", WoopsaValueType.Real, 28 | (sender) => property2Value, (sender, value) => property2Value = value.ToInt32()); 29 | 30 | Assert.AreEqual(coMessung1.Properties.Count(), 2); 31 | Assert.AreEqual(coMessung1.Properties.First().Value.ToDouble(), 1040.0); 32 | coMessung1.Properties.ByName("Variation").Value = 45; 33 | Assert.AreEqual(coMessung1.Properties.ByName("Variation").Value.ToInt32(), 45); 34 | Assert.AreEqual(coMessung1.Properties.ByName("Variation").Value.ToString(), "45"); 35 | (coMessung1.ByName("Variation") as IWoopsaProperty).Value = (WoopsaValue)36; 36 | Assert.AreEqual(coMessung1.Properties.ByName("Variation").Value.ToInt32(), 36); 37 | coMessung1.Properties["Variation"].Value = 5; 38 | Assert.AreEqual(property2Value, 5); 39 | int variation = coMessung1.Properties["Variation"].Value; 40 | Assert.AreEqual(variation, 5); 41 | WoopsaMethod method1 = new WoopsaMethod(coMessung1, "Calibrate", WoopsaValueType.Null, 42 | new WoopsaMethodArgumentInfo[] { 43 | new WoopsaMethodArgumentInfo("minLevel", WoopsaValueType.Real), 44 | new WoopsaMethodArgumentInfo("maxLevel", WoopsaValueType.Real) 45 | }, 46 | Calibrate); 47 | IWoopsaValue result = method1.Invoke(1.1, 5.5); 48 | Assert.AreEqual(result, WoopsaValue.Null); 49 | Assert.AreEqual(_minLevel, 1.1); 50 | Assert.AreEqual(_maxLevel, 5.5); 51 | } 52 | 53 | [TestMethod, TestCategory("Performance")] 54 | public void TestWoopsaObjectPerformance() 55 | { 56 | const int objectCount = 5000; 57 | const int accessCount = 50000; 58 | WoopsaRoot root = new WoopsaRoot(); 59 | for (int i = 0; i < objectCount; i++) 60 | { 61 | WoopsaObject newObject = new WoopsaObject(root, "Item" + i.ToString()); 62 | int x = i; 63 | new WoopsaProperty(newObject, "Data", WoopsaValueType.Integer, (p) => x); 64 | } 65 | Stopwatch watch = new Stopwatch(); 66 | watch.Start(); 67 | for (int i = 0; i < accessCount; i++) 68 | { 69 | int k = ((WoopsaProperty)(root.ByPath("Item" + (objectCount - 1).ToString() + "/Data"))).Value.ToInt32(); 70 | } 71 | Assert.IsTrue(watch.ElapsedMilliseconds < 1000); 72 | } 73 | 74 | #region Private Members 75 | 76 | private WoopsaValue Calibrate(System.Collections.Generic.IEnumerable arguments) 77 | { 78 | _minLevel = arguments.ElementAt(0).ToDouble(); 79 | _maxLevel = arguments.ElementAt(1).ToDouble(); 80 | return WoopsaValue.Null; 81 | } 82 | 83 | private double _minLevel, _maxLevel; 84 | 85 | #endregion 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Sources/DotNet/WoopsaTest/WoopsaTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Sources/Embedded/ArduinoDemoServer/woopsa-config.h: -------------------------------------------------------------------------------- 1 | #ifndef __WOOPSA_CONFIG_H_ 2 | #define __WOOPSA_CONFIG_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | // Woopsa uses these internally, allowing you to use Woopsa in a 11 | // thread-safe manner, or disabling interrupts. Just fill this in 12 | // if needed with whatever locking mechanism your environment has. 13 | #define WOOPSA_LOCK // disable interrupts 14 | #define WOOPSA_UNLOCK // enable interrupts 15 | 16 | // If you are on a system with very low memory, you can reduce the 17 | // buffer size that the Woopsa server uses internally. 18 | // This value changes the maximum length of URLs you can parse. 19 | // You should not go under 128 bytes to be 100% safe. 20 | #define WOOPSA_BUFFER_SIZE 256 21 | 22 | // Thanks microsoft for not supporting snprintf! 23 | #if defined(_MSC_VER) && _MSC_VER < 1900 24 | #define snprintf _snprintf 25 | #endif 26 | 27 | #define WOOPSA_ENABLE_STRINGS 28 | #define WOOPSA_ENABLE_METHODS 29 | 30 | // 99% of systems will have the standard C library but in case 31 | // you end up in the 1%, you can always re-define these functions 32 | // to work for you. 33 | #define WOOPSA_INTEGER_TO_PADDED_STRING(value, string, padding) sprintf(string, "%" #padding "d", value) 34 | #define WOOPSA_INTEGER_TO_STRING(value, string, max_length) snprintf(string, max_length-1, "%d", value) 35 | #define WOOPSA_REAL_TO_STRING(value, string, max_length) snprintf(string, max_length-1, "%f", value) 36 | 37 | #define WOOPSA_STRING_TO_INTEGER(value, string) (value = atoi(string)) 38 | #define WOOPSA_STRING_TO_FLOAT(value, string) (value = (float)atof(string)) 39 | 40 | #define WOOPSA_STRING_POSITION(haystack, needle) strstr(haystack, needle) 41 | #define WOOPSA_STRING_EQUAL(string1, string2) (strcmp(string1, string2) == 0) 42 | #define WOOPSA_STRING_LENGTH(string) strlen(string) 43 | #define WOOPSA_CHAR_TO_LOWER(character) (WoopsaChar8)tolower(character) 44 | #define WOOPSA_STRING_COPY(destination, source) strcpy(destination, source) 45 | #define WOOPSA_STRING_N_COPY(destination, source, n) strncpy(destination, source, n) 46 | 47 | #endif -------------------------------------------------------------------------------- /Sources/Embedded/ArduinoDemoServer/woopsa-server.h: -------------------------------------------------------------------------------- 1 | #ifndef __WOOPSA_H_ 2 | #define __WOOPSA_H_ 3 | 4 | #include "woopsa-config.h" 5 | 6 | typedef long WoopsaBufferSize; 7 | typedef short WoopsaInt16; 8 | typedef unsigned short WoopsaUInt16; 9 | typedef char WoopsaChar8; 10 | typedef unsigned char WoopsaUInt8; 11 | typedef void * WoopsaVoidPtr; 12 | 13 | typedef WoopsaChar8 WoopsaBuffer[WOOPSA_BUFFER_SIZE]; 14 | 15 | #ifdef WOOPSA_ENABLE_METHODS 16 | typedef void(*ptrMethodVoid)(void); 17 | typedef char*(*ptrMethodRetString)(void); 18 | typedef int(*ptrMethodRetInteger)(void); 19 | typedef float(*ptrMethodRetReal)(void); 20 | #endif 21 | 22 | // JsonData is not supported in Woopsa-C 23 | typedef enum { 24 | WOOPSA_TYPE_NULL, 25 | WOOPSA_TYPE_LOGICAL, 26 | WOOPSA_TYPE_INTEGER, 27 | WOOPSA_TYPE_REAL, 28 | WOOPSA_TYPE_TIME_SPAN, 29 | #ifdef WOOPSA_ENABLE_STRINGS 30 | WOOPSA_TYPE_DATE_TIME, 31 | WOOPSA_TYPE_TEXT, 32 | WOOPSA_TYPE_LINK, 33 | WOOPSA_TYPE_RESOURCE_URL 34 | #endif 35 | } WoopsaType; 36 | 37 | typedef struct { 38 | const WoopsaChar8 * name; 39 | union 40 | { 41 | void* data; 42 | ptrMethodVoid function; 43 | } address; 44 | WoopsaUInt8 type; 45 | WoopsaChar8 readOnly; 46 | WoopsaChar8 isMethod; 47 | WoopsaUInt8 size; 48 | } WoopsaEntry; 49 | 50 | typedef WoopsaBufferSize (*WoopsaRequestHandler)(WoopsaChar8*, WoopsaUInt8, WoopsaChar8*, WoopsaBufferSize); 51 | 52 | 53 | typedef struct { 54 | // The prefix for all Woopsa routes. Any client request 55 | // made without this prefix will pass the request to 56 | // the handleRequest function, if it exists 57 | const WoopsaChar8 * pathPrefix; 58 | // A function pointer to a function that accepts client 59 | // requests with a specified path and POST. 60 | // The function is in charge of copying content to the 61 | // content string, and should return an amount of bytes. 62 | // If the amount of bytes is 0, then the server will send 63 | // a 404 Not Found error. 64 | WoopsaRequestHandler requestHandler; 65 | // A list of entries to be published by Woopsa 66 | WoopsaEntry* entries; 67 | // A small buffer string used in various places in the server 68 | WoopsaBuffer buffer; 69 | } WoopsaServer; 70 | 71 | #define WOOPSA_BEGIN(woopsaDictionaryName) \ 72 | WoopsaEntry woopsaDictionaryName[] = \ 73 | { 74 | 75 | #define WOOPSA_END \ 76 | { (WoopsaChar8 *)NULL, { (void*)NULL }, 0, 0, 0, 0 }}; 77 | 78 | #define WOOPSA_PROPERTY_CUSTOM(variable, type, readonly) \ 79 | { #variable, { &variable }, type, readonly, 0, sizeof variable }, 80 | 81 | #define WOOPSA_PROPERTY_READONLY(variable, type) \ 82 | WOOPSA_PROPERTY_CUSTOM(variable, type, 1) 83 | 84 | #define WOOPSA_PROPERTY(variable, type) \ 85 | WOOPSA_PROPERTY_CUSTOM(variable, type, 0) 86 | 87 | #ifdef WOOPSA_ENABLE_METHODS 88 | #define WOOPSA_METHOD(method, returnType) \ 89 | {#method, { (void*)method }, returnType, 0, 1, 0 }, 90 | 91 | #define WOOPSA_METHOD_VOID(method) \ 92 | WOOPSA_METHOD(method, WOOPSA_TYPE_NULL) 93 | #endif 94 | 95 | #ifdef __cplusplus 96 | extern "C" 97 | { 98 | #endif 99 | 100 | 101 | 102 | // Creates a new Woopsa server using the specified prefix 103 | // and a list of entries to publish 104 | void WoopsaServerInit(WoopsaServer* server, const WoopsaChar8* prefix, WoopsaEntry entries[], WoopsaRequestHandler requestHandler); 105 | 106 | // Checks if the request contained in inputBuffer 107 | // is finished. This is useful in the case where 108 | // data is received in fragments for some reason. 109 | // You should always call this method on your buffer 110 | // before passing it to WoopsaHandleRequest 111 | #define WOOPSA_REQUEST_MORE_DATA_NEEDED 0 112 | #define WOOPSA_REQUEST_COMLETE 1 113 | WoopsaUInt8 WoopsaCheckRequestComplete(WoopsaServer* server, WoopsaChar8* inputBuffer, WoopsaUInt16 inputBufferLength); 114 | 115 | // Parses a request and prepares the reply as well 116 | // Returns: 117 | // WOOPSA_SUCCESS (0) = success 118 | // WOOPSA_CLIENT_REQUEST_ERROR (1) = the client made a bad request 119 | // WOPOSA_OTHER_ERROR (2) = something wrong happened inside Woopsa (it's our fault) 120 | #define WOOPSA_SUCCESS 0 121 | #define WOOPSA_CLIENT_REQUEST_ERROR 1 122 | #define WOOPSA_OTHER_ERROR 2 123 | #define WOOPSA_OTHER_RESPONSE 3 124 | 125 | WoopsaUInt8 WoopsaHandleRequest(WoopsaServer* server, const WoopsaChar8* inputBuffer, WoopsaBufferSize inputBufferLength, WoopsaChar8* outputBuffer, 126 | WoopsaBufferSize outputBufferLength, WoopsaBufferSize* responseLength); 127 | 128 | #ifdef __cplusplus 129 | } 130 | #endif 131 | 132 | #endif -------------------------------------------------------------------------------- /Sources/Embedded/DemoServer/DemoServer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../Server/woopsa-server.h" 4 | 5 | #ifdef _WIN32 6 | /* See http://stackoverflow.com/questions/12765743/getaddrinfo-on-win32 */ 7 | #ifndef _WIN32_WINNT 8 | #define _WIN32_WINNT 0x0501 /* Windows XP. */ 9 | #define CHECK_SOCKET(socket) \ 10 | (!(socket == INVALID_SOCKET)) 11 | #define EXIT_ERROR() \ 12 | exit(WSAGetLastError()) 13 | #endif 14 | #include 15 | #include 16 | #else 17 | /* Assume that any non-Windows platform uses POSIX-style sockets instead. */ 18 | #include 19 | #include 20 | #include 21 | #include /* Needed for getaddrinfo() and freeaddrinfo() */ 22 | #include /* Needed for close() */ 23 | #define CHECK_SOCKET(socket) \ 24 | (!(socket < 0)) 25 | #define EXIT_ERROR() \ 26 | exit(errno) 27 | #endif 28 | 29 | int sockInit(void) { 30 | #ifdef _WIN32 31 | WSADATA wsa_data; 32 | return WSAStartup(MAKEWORD(1, 1), &wsa_data); 33 | #else 34 | return 0; 35 | #endif 36 | } 37 | 38 | int sockQuit(void) { 39 | #ifdef _WIN32 40 | return WSACleanup(); 41 | #else 42 | return 0; 43 | #endif 44 | } 45 | 46 | /* Note: For POSIX, typedef SOCKET as an int. */ 47 | int sockClose(SOCKET sock) { 48 | int status = 0; 49 | #ifdef _WIN32 50 | status = shutdown(sock, SD_BOTH); 51 | if (status == 0) { status = closesocket(sock); } 52 | #else 53 | status = shutdown(sock, SHUT_RDWR); 54 | if (status == 0) { status = close(sock); } 55 | #endif 56 | 57 | return status; 58 | } 59 | 60 | float Temperature = 24.2f; 61 | char IsRaining = 1; 62 | int Altitude = 430; 63 | float Sensitivity = 0.5f; 64 | char City[20] = "Geneva"; 65 | float TimeSinceLastRain = 11; 66 | 67 | 68 | char weatherBuffer[20]; 69 | char* GetWeather() { 70 | sprintf(weatherBuffer, "sunny"); 71 | return weatherBuffer; 72 | } 73 | 74 | WOOPSA_BEGIN(woopsaEntries) 75 | WOOPSA_PROPERTY_READONLY(Temperature, WOOPSA_TYPE_REAL) 76 | WOOPSA_PROPERTY(IsRaining, WOOPSA_TYPE_LOGICAL) 77 | WOOPSA_PROPERTY(Altitude, WOOPSA_TYPE_INTEGER) 78 | WOOPSA_PROPERTY(Sensitivity, WOOPSA_TYPE_REAL) 79 | WOOPSA_PROPERTY(City, WOOPSA_TYPE_TEXT) 80 | WOOPSA_PROPERTY(TimeSinceLastRain, WOOPSA_TYPE_TIME_SPAN) 81 | WOOPSA_METHOD(GetWeather, WOOPSA_TYPE_TEXT) 82 | WOOPSA_END; 83 | 84 | #define WOOPSA_PORT 8000 85 | #define BUFFER_SIZE 1024 86 | 87 | 88 | WoopsaBufferSize ServeHTML(WoopsaChar8 path[], WoopsaUInt8 isPost, WoopsaChar8 dataBuffer[], WoopsaBufferSize dataBufferSize) { 89 | strcpy(dataBuffer, "Hello world!"); 90 | return (WoopsaBufferSize)strlen("Hellow world!"); 91 | } 92 | 93 | int main(int argc, char * argv[]) { 94 | SOCKET sock, clientSock; 95 | struct sockaddr_in addr; 96 | struct sockaddr clientAddr; 97 | char buffer[BUFFER_SIZE]; 98 | int clientAddrSize = 0, readBytes = 0; 99 | WoopsaServer server; 100 | WoopsaBufferSize responseLength; 101 | 102 | memset(buffer, 0, sizeof(buffer)); 103 | WoopsaServerInit(&server, "/woopsa/", woopsaEntries, ServeHTML); 104 | 105 | printf("Woopsa C library v0.1 demo server.\n"); 106 | 107 | if (sockInit() != 0) { 108 | printf("Error initializing sockets\n"); 109 | EXIT_ERROR(); 110 | } 111 | 112 | sock = socket(AF_INET, SOCK_STREAM, 0); 113 | if (!CHECK_SOCKET(sock)) { 114 | printf("Error creating socket\n"); 115 | EXIT_ERROR(); 116 | } 117 | 118 | addr.sin_family = AF_INET; 119 | addr.sin_addr.s_addr = INADDR_ANY; 120 | addr.sin_port = htons(WOOPSA_PORT); 121 | 122 | if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) { 123 | printf("Error binding socket\n"); 124 | EXIT_ERROR(); 125 | } 126 | 127 | listen(sock, 5); 128 | printf("Server listening on port %d\n", WOOPSA_PORT); 129 | 130 | while (1) { 131 | clientAddrSize = sizeof(struct sockaddr_in); 132 | clientSock = accept(sock, &clientAddr, &clientAddrSize); 133 | if (!CHECK_SOCKET(clientSock)) { 134 | printf("Received an invalid client socket.\n"); 135 | EXIT_ERROR(); 136 | } 137 | 138 | while (1) { 139 | readBytes = recv(clientSock, buffer + readBytes, sizeof(buffer), 0); 140 | 141 | if (readBytes == SOCKET_ERROR) { 142 | printf("Error %d", WSAGetLastError()); 143 | break; 144 | } 145 | 146 | if (readBytes == 0) { 147 | printf("Finished\n"); 148 | break; 149 | } 150 | 151 | if (WoopsaCheckRequestComplete(&server, buffer, sizeof(buffer)) != WOOPSA_REQUEST_COMLETE) { 152 | // If the request is not complete, it means more data needs 153 | // to be -added- to the buffer 154 | continue; 155 | } 156 | 157 | if (WoopsaHandleRequest(&server, buffer, sizeof(buffer), buffer, sizeof(buffer), &responseLength) >= WOOPSA_SUCCESS) { 158 | send(clientSock, buffer, responseLength, 0); 159 | } 160 | readBytes = 0; 161 | memset(buffer, 0, sizeof(buffer)); 162 | } 163 | } 164 | 165 | if (sockClose(sock) != 0) { 166 | printf("Error closing socket\n"); 167 | EXIT_ERROR(); 168 | } 169 | 170 | if (sockQuit() != 0) { 171 | printf("Error quitting sockets\n"); 172 | EXIT_ERROR(); 173 | } 174 | 175 | getchar(); 176 | 177 | return 0; 178 | } -------------------------------------------------------------------------------- /Sources/Embedded/DemoServer/DemoServer.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {08DBBF2F-BD66-4B8A-A35E-C8BD9A0257AA} 15 | DemoServer 16 | 17 | 18 | 19 | Application 20 | true 21 | v120 22 | MultiByte 23 | 24 | 25 | Application 26 | false 27 | v120 28 | true 29 | MultiByte 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Level4 45 | Disabled 46 | true 47 | _MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS 48 | 49 | 50 | true 51 | ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 52 | 53 | 54 | 55 | 56 | Level3 57 | MaxSpeed 58 | true 59 | true 60 | true 61 | _MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS 62 | 63 | 64 | true 65 | true 66 | true 67 | ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /Sources/Embedded/Server/Server.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {A211465F-C470-48FB-BB20-B7D8B9CA3E69} 15 | Server 16 | 17 | 18 | 19 | Makefile 20 | true 21 | v120 22 | MultiByte 23 | 24 | 25 | Makefile 26 | false 27 | v120 28 | true 29 | MultiByte 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | false 43 | xcopy /Y "$(ProjectDir)*.h" "$(ProjectDir)..\ArduinoDemoServer\" 44 | xcopy /Y "$(ProjectDir)*.c" "$(ProjectDir)..\ArduinoDemoServer\" 45 | 46 | 47 | 48 | Level3 49 | Disabled 50 | true 51 | 52 | 53 | true 54 | 55 | 56 | 57 | 58 | Level3 59 | MaxSpeed 60 | true 61 | true 62 | true 63 | 64 | 65 | true 66 | true 67 | true 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /Sources/Embedded/Server/woopsa-config.h: -------------------------------------------------------------------------------- 1 | #ifndef __WOOPSA_CONFIG_H_ 2 | #define __WOOPSA_CONFIG_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | // Woopsa uses these internally, allowing you to use Woopsa in a 11 | // thread-safe manner, or disabling interrupts. Just fill this in 12 | // if needed with whatever locking mechanism your environment has. 13 | #define WOOPSA_LOCK // disable interrupts 14 | #define WOOPSA_UNLOCK // enable interrupts 15 | 16 | // If you are on a system with very low memory, you can reduce the 17 | // buffer size that the Woopsa server uses internally. 18 | // This value changes the maximum length of URLs you can parse. 19 | // You should not go under 128 bytes to be 100% safe. 20 | #define WOOPSA_BUFFER_SIZE 256 21 | 22 | // Thanks microsoft for not supporting snprintf! 23 | #if defined(_MSC_VER) && _MSC_VER < 1900 24 | #define snprintf _snprintf 25 | #endif 26 | 27 | #define WOOPSA_ENABLE_STRINGS 28 | #define WOOPSA_ENABLE_METHODS 29 | 30 | // 99% of systems will have the standard C library but in case 31 | // you end up in the 1%, you can always re-define these functions 32 | // to work for you. 33 | #define WOOPSA_INTEGER_TO_PADDED_STRING(value, string, padding) sprintf(string, "%" #padding "d", value) 34 | #define WOOPSA_INTEGER_TO_STRING(value, string, max_length) snprintf(string, max_length-1, "%d", value) 35 | #define WOOPSA_REAL_TO_STRING(value, string, max_length) snprintf(string, max_length-1, "%f", value) 36 | 37 | #define WOOPSA_STRING_TO_INTEGER(value, string) (value = atoi(string)) 38 | #define WOOPSA_STRING_TO_FLOAT(value, string) (value = (float)atof(string)) 39 | 40 | #define WOOPSA_STRING_POSITION(haystack, needle) strstr(haystack, needle) 41 | #define WOOPSA_STRING_EQUAL(string1, string2) (strcmp(string1, string2) == 0) 42 | #define WOOPSA_STRING_LENGTH(string) strlen(string) 43 | #define WOOPSA_CHAR_TO_LOWER(character) (WoopsaChar8)tolower(character) 44 | #define WOOPSA_STRING_COPY(destination, source) strcpy(destination, source) 45 | #define WOOPSA_STRING_N_COPY(destination, source, n) strncpy(destination, source, n) 46 | 47 | #endif -------------------------------------------------------------------------------- /Sources/Embedded/Server/woopsa-server.h: -------------------------------------------------------------------------------- 1 | #ifndef __WOOPSA_H_ 2 | #define __WOOPSA_H_ 3 | 4 | #include "woopsa-config.h" 5 | 6 | typedef long WoopsaBufferSize; 7 | typedef short WoopsaInt16; 8 | typedef unsigned short WoopsaUInt16; 9 | typedef char WoopsaChar8; 10 | typedef unsigned char WoopsaUInt8; 11 | typedef void * WoopsaVoidPtr; 12 | 13 | typedef WoopsaChar8 WoopsaBuffer[WOOPSA_BUFFER_SIZE]; 14 | 15 | #ifdef WOOPSA_ENABLE_METHODS 16 | typedef void(*ptrMethodVoid)(void); 17 | typedef char*(*ptrMethodRetString)(void); 18 | typedef int(*ptrMethodRetInteger)(void); 19 | typedef float(*ptrMethodRetReal)(void); 20 | #endif 21 | 22 | // JsonData is not supported in Woopsa-C 23 | typedef enum { 24 | WOOPSA_TYPE_NULL, 25 | WOOPSA_TYPE_LOGICAL, 26 | WOOPSA_TYPE_INTEGER, 27 | WOOPSA_TYPE_REAL, 28 | WOOPSA_TYPE_TIME_SPAN, 29 | #ifdef WOOPSA_ENABLE_STRINGS 30 | WOOPSA_TYPE_DATE_TIME, 31 | WOOPSA_TYPE_TEXT, 32 | WOOPSA_TYPE_LINK, 33 | WOOPSA_TYPE_RESOURCE_URL 34 | #endif 35 | } WoopsaType; 36 | 37 | typedef struct { 38 | const WoopsaChar8 * name; 39 | union 40 | { 41 | void* data; 42 | ptrMethodVoid function; 43 | } address; 44 | WoopsaUInt8 type; 45 | WoopsaChar8 readOnly; 46 | WoopsaChar8 isMethod; 47 | WoopsaUInt8 size; 48 | } WoopsaEntry; 49 | 50 | typedef WoopsaBufferSize (*WoopsaRequestHandler)(WoopsaChar8*, WoopsaUInt8, WoopsaChar8*, WoopsaBufferSize); 51 | 52 | 53 | typedef struct { 54 | // The prefix for all Woopsa routes. Any client request 55 | // made without this prefix will pass the request to 56 | // the handleRequest function, if it exists 57 | const WoopsaChar8 * pathPrefix; 58 | // A function pointer to a function that accepts client 59 | // requests with a specified path and POST. 60 | // The function is in charge of copying content to the 61 | // content string, and should return an amount of bytes. 62 | // If the amount of bytes is 0, then the server will send 63 | // a 404 Not Found error. 64 | WoopsaRequestHandler requestHandler; 65 | // A list of entries to be published by Woopsa 66 | WoopsaEntry* entries; 67 | // A small buffer string used in various places in the server 68 | WoopsaBuffer buffer; 69 | } WoopsaServer; 70 | 71 | #define WOOPSA_BEGIN(woopsaDictionaryName) \ 72 | WoopsaEntry woopsaDictionaryName[] = \ 73 | { 74 | 75 | #define WOOPSA_END \ 76 | { (WoopsaChar8 *)NULL, { (void*)NULL }, 0, 0, 0, 0 }}; 77 | 78 | #define WOOPSA_PROPERTY_CUSTOM(variable, type, readonly) \ 79 | { #variable, { &variable }, type, readonly, 0, sizeof variable }, 80 | 81 | #define WOOPSA_PROPERTY_READONLY(variable, type) \ 82 | WOOPSA_PROPERTY_CUSTOM(variable, type, 1) 83 | 84 | #define WOOPSA_PROPERTY(variable, type) \ 85 | WOOPSA_PROPERTY_CUSTOM(variable, type, 0) 86 | 87 | #ifdef WOOPSA_ENABLE_METHODS 88 | #define WOOPSA_METHOD(method, returnType) \ 89 | {#method, { (void*)method }, returnType, 0, 1, 0 }, 90 | 91 | #define WOOPSA_METHOD_VOID(method) \ 92 | WOOPSA_METHOD(method, WOOPSA_TYPE_NULL) 93 | #endif 94 | 95 | #ifdef __cplusplus 96 | extern "C" 97 | { 98 | #endif 99 | 100 | 101 | 102 | // Creates a new Woopsa server using the specified prefix 103 | // and a list of entries to publish 104 | void WoopsaServerInit(WoopsaServer* server, const WoopsaChar8* prefix, WoopsaEntry entries[], WoopsaRequestHandler requestHandler); 105 | 106 | // Checks if the request contained in inputBuffer 107 | // is finished. This is useful in the case where 108 | // data is received in fragments for some reason. 109 | // You should always call this method on your buffer 110 | // before passing it to WoopsaHandleRequest 111 | #define WOOPSA_REQUEST_MORE_DATA_NEEDED 0 112 | #define WOOPSA_REQUEST_COMLETE 1 113 | WoopsaUInt8 WoopsaCheckRequestComplete(WoopsaServer* server, WoopsaChar8* inputBuffer, WoopsaUInt16 inputBufferLength); 114 | 115 | // Parses a request and prepares the reply as well 116 | // Returns: 117 | // WOOPSA_SUCCESS (0) = success 118 | // WOOPSA_CLIENT_REQUEST_ERROR (1) = the client made a bad request 119 | // WOPOSA_OTHER_ERROR (2) = something wrong happened inside Woopsa (it's our fault) 120 | #define WOOPSA_SUCCESS 0 121 | #define WOOPSA_CLIENT_REQUEST_ERROR 1 122 | #define WOOPSA_OTHER_ERROR 2 123 | #define WOOPSA_OTHER_RESPONSE 3 124 | 125 | WoopsaUInt8 WoopsaHandleRequest(WoopsaServer* server, const WoopsaChar8* inputBuffer, WoopsaBufferSize inputBufferLength, WoopsaChar8* outputBuffer, 126 | WoopsaBufferSize outputBufferLength, WoopsaBufferSize* responseLength); 127 | 128 | #ifdef __cplusplus 129 | } 130 | #endif 131 | 132 | #endif -------------------------------------------------------------------------------- /Sources/Embedded/WoopsaEmbedded.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.21005.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Server", "Server\Server.vcxproj", "{A211465F-C470-48FB-BB20-B7D8B9CA3E69}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DemoServer", "DemoServer\DemoServer.vcxproj", "{08DBBF2F-BD66-4B8A-A35E-C8BD9A0257AA}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Win32 = Debug|Win32 13 | Release|Win32 = Release|Win32 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {A211465F-C470-48FB-BB20-B7D8B9CA3E69}.Debug|Win32.ActiveCfg = Debug|Win32 17 | {A211465F-C470-48FB-BB20-B7D8B9CA3E69}.Debug|Win32.Build.0 = Debug|Win32 18 | {A211465F-C470-48FB-BB20-B7D8B9CA3E69}.Release|Win32.ActiveCfg = Release|Win32 19 | {A211465F-C470-48FB-BB20-B7D8B9CA3E69}.Release|Win32.Build.0 = Release|Win32 20 | {08DBBF2F-BD66-4B8A-A35E-C8BD9A0257AA}.Debug|Win32.ActiveCfg = Debug|Win32 21 | {08DBBF2F-BD66-4B8A-A35E-C8BD9A0257AA}.Debug|Win32.Build.0 = Debug|Win32 22 | {08DBBF2F-BD66-4B8A-A35E-C8BD9A0257AA}.Release|Win32.ActiveCfg = Release|Win32 23 | {08DBBF2F-BD66-4B8A-A35E-C8BD9A0257AA}.Release|Win32.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /Sources/Embedded/build-windows.bat: -------------------------------------------------------------------------------- 1 | devenv WoopsaEmbedded.sln /build Release /project Server 2 | devenv WoopsaEmbedded.sln /build Release /project DemoServer -------------------------------------------------------------------------------- /Sources/JavaScript/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /Sources/JavaScript/browser/demo/demo.js: -------------------------------------------------------------------------------- 1 | (function ($){ 2 | $(document).ready(function (){ 3 | //var woopsa = new WoopsaClient("/woopsa", jQuery); 4 | //The more complicated, more customizeable way 5 | /* 6 | woopsa.createSubscriptionChannel(200, function (channelId) { 7 | console.log("Created subscription channel with id " + channelId); 8 | this.registerSubscription("/Child1/Property1", function (value) { 9 | console.log("The new value is " + value); 10 | }) 11 | }) 12 | */ 13 | 14 | var subscriptionRegistered = null; 15 | 16 | $("#urlForm").submit(function (){ 17 | woopsa = new WoopsaClient($("#serverUrl").val(), jQuery); 18 | 19 | //The very easy way of making a subscription 20 | woopsa.onChange($("#subscribePath").val(), function (value){ 21 | console.log("Received notification, new value = " + value); 22 | $("#variableValue").html(value); 23 | },1,1, function (subscription){ 24 | console.log("Created ok"); 25 | console.log(subscription); 26 | subscriptionRegistered = subscription; 27 | }); 28 | 29 | $("input, textarea").each(function (){ 30 | $(this).removeAttr("disabled"); 31 | }) 32 | 33 | $("#subscribePath").attr("disabled","disabled"); 34 | $("#urlForm input").each(function (){ $(this).attr("disabled","disabled")}); 35 | 36 | woopsa.onError(function (type, errorThown){ 37 | $(".log").prepend("" + (new Date().toUTCString()) + ": " + type + " - " + errorThown + "
"); 38 | }) 39 | 40 | return false; 41 | }); 42 | 43 | $("#readForm").submit(function (){ 44 | woopsa.read($("#readPath").val(), function(value){ 45 | $("#readValue").html(value); 46 | }); 47 | return false; 48 | }); 49 | 50 | $("#writeForm").submit(function (){ 51 | woopsa.write($("#writePath").val(), $("#writeValue").val(), function(value){ 52 | console.log(value); 53 | }); 54 | return false; 55 | }); 56 | 57 | $("#invokeForm").submit(function (){ 58 | var invArgs = {}; 59 | invArgs[$("#invokeArgumentName").val()] = $("#invokeArgumentValue").val(); 60 | woopsa.invoke($("#invokePath").val(), invArgs, function (value){ 61 | $("#invokeValue").html(JSON.stringify(value, null, 2)); 62 | }); 63 | return false; 64 | }) 65 | 66 | $("#metaForm").submit(function (){ 67 | woopsa.meta($("#metaPath").val(), function (value){ 68 | $("#metaValue").html(JSON.stringify(value, null, 2)); 69 | }) 70 | return false; 71 | }); 72 | }); 73 | })(jQuery); -------------------------------------------------------------------------------- /Sources/JavaScript/nodejs/README.md: -------------------------------------------------------------------------------- 1 | # Woopsa 2 | This is the node.js Woopsa module. To find out more about Woopsa and get the C# / Embedded C or jQuery implementation, go to http://www.woopsa.org! 3 | 4 | Woopsa is a protocol that's simple, lightweight, free, open-source, web and object-oriented, publish-subscribe, real-time capable and Industry 4.0 ready. It contributes to the revolution of the Internet of Things. 5 | 6 | Woopsa allows you to share the complete object model of your application in a way that's similar to OPC-UA. It's based on rock-solid foundations such as HTTP and JSON, which makes it work on the web out-of-the-box. Our mission is to get Woopsa on as many platforms as possible. Today, C# and JavaScript implementations exist, but there are much more to come! 7 | 8 | As a node module, Woopsa is very useful if you wish to quickly create a RESTful API. 9 | 10 | On the server-side, just give Woopsa any JavaScript object with properties and functions, and the library will create a RESTful API allowing you to read/write properties and call functions, automagically! 11 | 12 | ````js 13 | var woopsa = require('woopsa'); 14 | var weatherStation = { 15 | Temperature: 24.2, 16 | IsRaining: false, 17 | Sensitivity: 0.5, 18 | Altitude: 430, 19 | City: "Geneva", 20 | Time: new Date(), 21 | GetWeatherAtDate: function (date){ 22 | var date = new Date(date); 23 | if ( date.getDay() === 1 ) // monday? 24 | return "rainy"; 25 | else 26 | return "sunny"; 27 | }, 28 | Thermostat: { 29 | SetPoint: 24.0 30 | } 31 | } 32 | var server = new woopsa.Server(weatherStation, {port: 80}); 33 | ```` 34 | 35 | To give it a test, just go on `http://{ip-of-your-server}/woopsa/read/Temperature` and see the magic! Woopsa is fully RESTful which means you can easily test it with your browser. It publishes your entire object's structure, and you can see all the data it publishes by going on `http://{ip-of-your-server}/woopsa/meta/` . 36 | 37 | On the client-side, just get the Woopsa jQuery/browser library on http://www.woopsa.org and you can immediately work with your object: 38 | ````js 39 | var woopsa = new WoopsaClient("http://{ip-of-your-server}/woopsa", jQuery); 40 | woopsa.read("/Temperature", function(result){ 41 | //result = 24.2 42 | }); 43 | woopsa.invoke("/GetWeatherAtDate", {date: new Date(2016,0,1)}, function(result){ 44 | //result = sunny (jan. 1st of 2016 was not a monday) 45 | } 46 | ```` 47 | 48 | Woopsa uses expressjs in the background, which means it can easily plug itself into your already existing server. We have a very detailed "Getting Started" guide [here](http://www.woopsa.org/get-started/) which explains how to do just that. 49 | 50 | ## Getting started 51 | Our [Getting Started](http://www.woopsa.org/get-started/) tutorial allows you to get started quickly with Woopsa. It's really easy and we promise you'll be convinced! 52 | -------------------------------------------------------------------------------- /Sources/JavaScript/nodejs/adapter.js: -------------------------------------------------------------------------------- 1 | var types = require('./types'); 2 | var exceptions = require('./exceptions'); 3 | var woopsaUtils = require('./woopsa-utils'); 4 | 5 | exports.generateMetaObject = function (element){ 6 | var metaObject = { 7 | Name: element.getName(), 8 | Properties: [], 9 | Methods: [], 10 | Items: [] 11 | }; 12 | var items = element.getItems(); 13 | for ( var i in items ){ 14 | metaObject.Items.push(items[i].getName()); 15 | } 16 | var properties = element.getProperties(); 17 | for ( var i in properties ){ 18 | var property = properties[i]; 19 | metaObject.Properties.push({ 20 | Name: property.getName(), 21 | Type: property.getType(), 22 | ReadOnly: (typeof property.write === 'undefined')?true:false 23 | }) 24 | } 25 | var methods = element.getMethods(); 26 | for ( var i in methods ){ 27 | var method = methods[i]; 28 | var newMethod = { 29 | Name: method.getName(), 30 | ReturnType: method.getReturnType(), 31 | ArgumentInfos: [] 32 | } 33 | var argumentInfos = method.getArgumentInfos(); 34 | for ( var j in argumentInfos ){ 35 | newMethod.ArgumentInfos.push({ 36 | Name: argumentInfos[j].getName(), 37 | Type: argumentInfos[j].getType() 38 | }) 39 | } 40 | metaObject.Methods.push(newMethod); 41 | } 42 | return metaObject; 43 | } 44 | 45 | exports.readProperty = function (property, done){ 46 | property.read(function (readResult){ 47 | done({ 48 | Value: readResult, 49 | Type: property.getType() 50 | }); 51 | }); 52 | } 53 | 54 | exports.writeProperty = function (property, value, done){ 55 | if ( typeof property.write === 'undefined' ){ 56 | throw new exceptions.WoopsaInvalidOperationException("Cannot write to read-only property " + element.getName()); 57 | }else{ 58 | value = woopsaUtils.convertTo(value, property.getType()) 59 | property.write(value, function (writeResult){ 60 | done({ 61 | Value: writeResult, 62 | Type: property.getType() 63 | }); 64 | }); 65 | 66 | } 67 | } 68 | 69 | exports.invokeMethod = function (method, args, done){ 70 | var processedArguments = processArguments(method, args); 71 | method.invoke(processedArguments, function (result, error){ 72 | if ( typeof error === 'undefined' ){ 73 | done({ 74 | Value: result, 75 | Type: method.getReturnType() 76 | }); 77 | }else{ 78 | done(null, error); 79 | } 80 | }); 81 | } 82 | 83 | function processArguments(method, methodArguments){ 84 | // Convert the name-value pair methodArguments into a simple array in the right order 85 | var argumentInfos = method.getArgumentInfos(); 86 | var args = []; 87 | for ( var i in argumentInfos ){ 88 | var argInfo = argumentInfos[i]; 89 | for ( var key in methodArguments ){ 90 | if ( argInfo.getName() === key ){ 91 | value = woopsaUtils.convertTo(methodArguments[key], argInfo.getType()) 92 | args.push(value); 93 | } 94 | } 95 | } 96 | if ( args.length != argumentInfos.length ){ 97 | throw new exceptions.WoopsaInvalidOperationException("Wrong parameters for WoopsaMethod " + method.getName()); 98 | } 99 | return args; 100 | } -------------------------------------------------------------------------------- /Sources/JavaScript/nodejs/demos/simple-server.js: -------------------------------------------------------------------------------- 1 | var woopsa = require('../index'); 2 | 3 | // Create a test object Weather Station 4 | var weatherStation = { 5 | Temperature: 24.2, 6 | IsRaining: false, 7 | Sensitivity: 0.5, 8 | Altitude: 430, 9 | City: "Geneva", 10 | Time: new Date(), 11 | GetWeatherAtDate: function (date){ 12 | if ( date.getDay() === 1 ) 13 | return "rainy"; 14 | else 15 | return "sunny"; 16 | }, 17 | Thermostat: { 18 | SetPoint: 24.0 19 | } 20 | } 21 | 22 | var server = new woopsa.Server(weatherStation, { 23 | port: 80, 24 | listenCallback: function (err){ 25 | if ( !err ){ 26 | console.log("Woopsa server listening on http://localhost:%d", server.options.port); 27 | console.log("Some examples of what you can do directly from your browser:"); 28 | console.log(" * View the object hierarchy of the root object:"); 29 | console.log(" http://localhost:%d%smeta/", server.options.port, server.options.pathPrefix); 30 | console.log(" * Read the value of a property:"); 31 | console.log(" http://localhost:%d%sread/Temperature", server.options.port, server.options.pathPrefix); 32 | }else{ 33 | console.log("Error"); 34 | } 35 | }, 36 | // The typer function allows us to specify the actual 37 | // Woopsa type for a WoopsaElement when we can't guess (infer) 38 | // it in JavaScript. For example, all function parameters 39 | // and return types are set to Text when using the reflector. 40 | // By returning DateTime for /GetWeatherAtDate/date, we 41 | // allow the library to directly send us a JavaScript 42 | // Date object. 43 | typer: function (path, inferredType){ 44 | if ( path === "/GetWeatherAtDate/date" ) 45 | return "DateTime"; 46 | else 47 | return inferredType; 48 | } 49 | }); 50 | 51 | server.httpServer.on('error', function (err){ 52 | console.log("Error: Could not start Woopsa Server. Most likely because an application is already listening on port %d.", server.options.port); 53 | console.log("Known culprits:"); 54 | console.log(" - On Windows 10, IIS is on by default on some configurations."); 55 | console.log(" - Skype"); 56 | console.log(" - Apache, nginx, etc."); 57 | console.log("%s", err); 58 | }); 59 | 60 | // Make the temperature of our WeatherStation fluctuate randomly 61 | setInterval(function (){ 62 | weatherStation.Temperature += Math.random() - 0.5; 63 | }, 20); 64 | -------------------------------------------------------------------------------- /Sources/JavaScript/nodejs/exceptions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class A generic exception type for when something went wrong. 3 | */ 4 | exports.WoopsaException = function (message){ 5 | this.Type = "WoopsaException"; 6 | this.Message = message; 7 | this.Error = true; 8 | } 9 | 10 | /** @class Thrown when the client requests something that doesn't exist in the hierarchy. */ 11 | exports.WoopsaNotFoundException = function (message){ 12 | this.Type = "WoopsaNotFoundException"; 13 | this.Message = message; 14 | this.Error = true; 15 | } 16 | 17 | /** @class Thrown when the client tries to do something forbidden like writing a Text to an Integer */ 18 | exports.WoopsaInvalidOperationException = function (message){ 19 | this.Type = "WoopsaInvalidOperationException"; 20 | this.Message = message; 21 | this.Error = true; 22 | } 23 | 24 | /** @class Thrown when notifications were lost and the client needs to acknowledge */ 25 | exports.WoopsaNotificationsLostException = function (message){ 26 | this.Type = "WoopsaNotificationsLostException"; 27 | this.Message = message; 28 | this.Error = true; 29 | } 30 | 31 | /** @class Thrown when the client references an inexisting Subscription Channel */ 32 | exports.WoopsaInvalidSubscriptionChannelException = function (message){ 33 | this.Type = "WoopsaInvalidSubscriptionChannelException"; 34 | this.Message = message; 35 | this.Error = true; 36 | } -------------------------------------------------------------------------------- /Sources/JavaScript/nodejs/extensions/multi-request.js: -------------------------------------------------------------------------------- 1 | var adapter = require('../adapter'); 2 | var types = require('../types'); 3 | var exceptions = require('../exceptions'); 4 | var woopsaUtils = require('../woopsa-utils'); 5 | 6 | /** 7 | * @class A service that allows clients to make multiple requests 8 | * at the same time. 9 | * @param {WoopsaObject} woopsaObject The WoopsaObject that this 10 | * method will be added to. 11 | * This should be your root 12 | * object. 13 | */ 14 | var MultiRequestHandler = function MultiRequestHandler(woopsaObject){ 15 | this._multiRequest = new types.WoopsaMethodAsync("MultiRequest", "JsonData", multiRequest.bind(this), [ 16 | {"Requests": "JsonData"} 17 | ]); 18 | woopsaObject.addMethod(this._multiRequest); 19 | 20 | function multiRequest(requests, done){ 21 | if ( !isArray(requests) ){ 22 | throw new exceptions.WoopsaInvalidOperationException("MultiRequest needs an array of requests."); 23 | } 24 | var results = []; 25 | var countAsync = 0; 26 | var doneAsync = 0; 27 | for ( var i in requests ){ 28 | if ( requests[i].Verb !== 'meta' ){ 29 | countAsync++; 30 | } 31 | } 32 | for ( var i in requests ){ 33 | var request = requests[i]; 34 | var element = woopsaUtils.getByPath(woopsaObject, request.Path); 35 | var newResult = {Id: request.id}; 36 | results.push(newResult); 37 | if ( request.Verb === 'meta' ){ 38 | newResult.Result = adapter.generateMetaObject(element); 39 | }else if ( request.Verb === 'read' ){ 40 | adapter.readProperty(element, resultCallback.bind(this, results.length-1)); 41 | }else if ( request.Verb === 'write' ){ 42 | adapter.writeProperty(element, request.Value, resultCallback.bind(this, results.length-1)); 43 | }else if ( request.Verb === 'invoke' ){ 44 | adapter.invokeMethod(element, request.Arguments, resultCallback.bind(this, results.length-1)) 45 | }else{ 46 | throw new exceptions.WoopsaInvalidOperationException("Invalid MultiRequest verb " + request.Verb); 47 | } 48 | } 49 | if ( countAsync === 0 ){ 50 | // If there were no invokes, we can immediately 51 | // send the result. 52 | // Otherwise, the done callback will be called 53 | // when all invoke's are done. 54 | done(results); 55 | } 56 | 57 | function resultCallback(resultIndex, value, error){ 58 | doneAsync++; 59 | if ( typeof error !== 'undefined' ) 60 | results[resultIndex].Result = error; 61 | else 62 | results[resultIndex].Result = value; 63 | if ( countAsync === doneAsync ){ 64 | done(results); 65 | } 66 | } 67 | } 68 | } 69 | 70 | function isArray(value){ 71 | return value && 72 | typeof value === 'object' && 73 | typeof value.length === 'number' && 74 | typeof value.splice === 'function' && 75 | !(value.propertyIsEnumerable('length')); 76 | } 77 | 78 | exports.MultiRequestHandler = MultiRequestHandler; -------------------------------------------------------------------------------- /Sources/JavaScript/nodejs/index.js: -------------------------------------------------------------------------------- 1 | var server = require('./server'); 2 | var types = require('./types'); 3 | var exceptions = require('./exceptions'); 4 | var reflector = require('./reflector'); 5 | var subscriptionService = require('./extensions/subscription-service'); 6 | var woopsaUtils = require('./woopsa-utils'); 7 | 8 | exports.Server = server.Server; 9 | exports.Types = types; 10 | exports.Exceptions = exceptions; 11 | exports.Reflector = reflector.Reflector; 12 | exports.SubscriptionService = subscriptionService.SubscriptionService; 13 | exports.Utils = woopsaUtils; -------------------------------------------------------------------------------- /Sources/JavaScript/nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "woopsa", 3 | "version": "1.1.9", 4 | "description": "Woopsa is the first fully open-source object-oriented protocol for automation and the Internet of Things", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Woopsa Protocol ", 10 | "license": "MIT", 11 | "bugs": { 12 | "url": "https://github.com/woopsa-protocol/Woopsa/issues" 13 | }, 14 | "homepage": "https://www.woopsa.org", 15 | "dependencies": { 16 | "basic-auth": "^1.0.3", 17 | "body-parser": "^1.14.2", 18 | "express": "^4.13.3", 19 | "get-parameter-names": "^0.2.0" 20 | } 21 | } 22 | --------------------------------------------------------------------------------