Compare commits
12 Commits
c2539d2250
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ffee0ec63 | ||
|
|
21081cc7ce | ||
|
|
65a994f1b2 | ||
| 5449c3e83d | |||
| 5bebbf311c | |||
|
|
8788e3d1d3 | ||
|
|
217a301109 | ||
|
|
80cd5cb9ae | ||
|
|
9f86b58aba | ||
|
|
5d78c9be58 | ||
|
|
d48ea94471 | ||
|
|
c863c66236 |
@@ -1,32 +1,43 @@
|
|||||||
{
|
{
|
||||||
// Displayed name
|
// Displayed name
|
||||||
"name": "IoT-Simulator",
|
"name": "IoT-Simulator",
|
||||||
|
|
||||||
// Image to be used
|
// Image to be used
|
||||||
"image": "swift",
|
"image": "swift",
|
||||||
|
|
||||||
// Env in container
|
// Env in container
|
||||||
"containerEnv": {
|
"containerEnv": {
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Mounts in container
|
// Customization
|
||||||
"mounts": [
|
"customizations": {
|
||||||
{
|
"vscode": {
|
||||||
"source": "${localWorkspaceFolder}",
|
"extensions": [
|
||||||
"target": "/workspace",
|
"sswg.swift-lang",
|
||||||
"type": "bind"
|
"fabiospampinato.vscode-highlight",
|
||||||
}
|
"fabiospampinato.vscode-todo-plus"
|
||||||
],
|
]
|
||||||
|
}
|
||||||
// The WorkspaceFolder inside container
|
},
|
||||||
"workspaceFolder": "/workspace",
|
|
||||||
|
// Mounts in container
|
||||||
// RunArgs
|
"mounts": [
|
||||||
"runArgs": [
|
{
|
||||||
"--name",
|
"source": "${localWorkspaceFolder}",
|
||||||
"IoT-Simulator-Vapor"
|
"target": "/workspace",
|
||||||
]
|
"type": "bind"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// The WorkspaceFolder inside container
|
||||||
|
"workspaceFolder": "/workspace",
|
||||||
|
|
||||||
|
// RunArgs
|
||||||
|
"runArgs": [
|
||||||
|
"--name",
|
||||||
|
"IoT-Simulator-Vapor"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
235
.gitignore
vendored
235
.gitignore
vendored
@@ -1,116 +1,119 @@
|
|||||||
##############################
|
##############################
|
||||||
## macOS
|
## macOS
|
||||||
##############################
|
##############################
|
||||||
# General
|
# General
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.AppleDouble
|
.AppleDouble
|
||||||
.LSOverride
|
.LSOverride
|
||||||
|
|
||||||
# Icon must end with two \r
|
# Icon must end with two \r
|
||||||
Icon
|
Icon
|
||||||
|
|
||||||
|
|
||||||
# Thumbnails
|
# Thumbnails
|
||||||
._*
|
._*
|
||||||
|
|
||||||
# Files that might appear in the root of a volume
|
# Files that might appear in the root of a volume
|
||||||
.DocumentRevisions-V100
|
.DocumentRevisions-V100
|
||||||
.fseventsd
|
.fseventsd
|
||||||
.Spotlight-V100
|
.Spotlight-V100
|
||||||
.TemporaryItems
|
.TemporaryItems
|
||||||
.Trashes
|
.Trashes
|
||||||
.VolumeIcon.icns
|
.VolumeIcon.icns
|
||||||
.com.apple.timemachine.donotpresent
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
# Directories potentially created on remote AFP share
|
# Directories potentially created on remote AFP share
|
||||||
.AppleDB
|
.AppleDB
|
||||||
.AppleDesktop
|
.AppleDesktop
|
||||||
Network Trash Folder
|
Network Trash Folder
|
||||||
Temporary Items
|
Temporary Items
|
||||||
.apdisk
|
.apdisk
|
||||||
|
|
||||||
##############################
|
##############################
|
||||||
## Swift
|
## Swift
|
||||||
##############################
|
##############################
|
||||||
|
|
||||||
# Xcode
|
# Xcode
|
||||||
#
|
#
|
||||||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||||
|
|
||||||
## User settings
|
## User settings
|
||||||
xcuserdata/
|
xcuserdata/
|
||||||
|
|
||||||
## Obj-C/Swift specific
|
## Obj-C/Swift specific
|
||||||
*.hmap
|
*.hmap
|
||||||
|
|
||||||
## App packaging
|
## App packaging
|
||||||
*.ipa
|
*.ipa
|
||||||
*.dSYM.zip
|
*.dSYM.zip
|
||||||
*.dSYM
|
*.dSYM
|
||||||
|
|
||||||
## Playgrounds
|
## Playgrounds
|
||||||
timeline.xctimeline
|
timeline.xctimeline
|
||||||
playground.xcworkspace
|
playground.xcworkspace
|
||||||
|
|
||||||
# Swift Package Manager
|
# Swift Package Manager
|
||||||
#
|
#
|
||||||
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
|
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
|
||||||
# Packages/
|
# Packages/
|
||||||
# Package.pins
|
# Package.pins
|
||||||
# Package.resolved
|
# Package.resolved
|
||||||
# *.xcodeproj
|
# *.xcodeproj
|
||||||
#
|
#
|
||||||
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
|
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
|
||||||
# hence it is not needed unless you have added a package configuration file to your project
|
# hence it is not needed unless you have added a package configuration file to your project
|
||||||
# .swiftpm
|
# .swiftpm
|
||||||
|
|
||||||
.build/
|
.build/
|
||||||
|
|
||||||
# CocoaPods
|
# CocoaPods
|
||||||
#
|
#
|
||||||
# We recommend against adding the Pods directory to your .gitignore. However
|
# We recommend against adding the Pods directory to your .gitignore. However
|
||||||
# you should judge for yourself, the pros and cons are mentioned at:
|
# you should judge for yourself, the pros and cons are mentioned at:
|
||||||
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||||
#
|
#
|
||||||
# Pods/
|
# Pods/
|
||||||
#
|
#
|
||||||
# Add this line if you want to avoid checking in source code from the Xcode workspace
|
# Add this line if you want to avoid checking in source code from the Xcode workspace
|
||||||
# *.xcworkspace
|
# *.xcworkspace
|
||||||
|
|
||||||
# Carthage
|
# Carthage
|
||||||
#
|
#
|
||||||
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
||||||
# Carthage/Checkouts
|
# Carthage/Checkouts
|
||||||
|
|
||||||
Carthage/Build/
|
Carthage/Build/
|
||||||
|
|
||||||
# fastlane
|
# fastlane
|
||||||
#
|
#
|
||||||
# It is recommended to not store the screenshots in the git repo.
|
# It is recommended to not store the screenshots in the git repo.
|
||||||
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
|
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
|
||||||
# For more information about the recommended setup visit:
|
# For more information about the recommended setup visit:
|
||||||
# https://docs.fastlane.tools/best-practices/source-control/#source-control
|
# https://docs.fastlane.tools/best-practices/source-control/#source-control
|
||||||
|
|
||||||
fastlane/report.xml
|
fastlane/report.xml
|
||||||
fastlane/Preview.html
|
fastlane/Preview.html
|
||||||
fastlane/screenshots/**/*.png
|
fastlane/screenshots/**/*.png
|
||||||
fastlane/test_output
|
fastlane/test_output
|
||||||
|
|
||||||
##############################
|
##############################
|
||||||
## Vapor
|
## Vapor
|
||||||
##############################
|
##############################
|
||||||
|
|
||||||
Packages
|
Packages
|
||||||
.build
|
.build
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.xcodeproj
|
*.xcodeproj
|
||||||
DerivedData/
|
DerivedData/
|
||||||
Package.resolved
|
Package.resolved
|
||||||
.swiftpm
|
.swiftpm
|
||||||
Tests/LinuxMain.swift
|
Tests/LinuxMain.swift
|
||||||
.bash_history
|
.bash_history
|
||||||
.cache/
|
.cache/
|
||||||
|
|
||||||
# API Docs Generation
|
# API Docs Generation
|
||||||
generate-package-api-docs.swift
|
generate-package-api-docs.swift
|
||||||
theme-settings.json
|
theme-settings.json
|
||||||
|
|
||||||
|
# ---> Private
|
||||||
|
**/Private/PrivateKeysP256/*.pem
|
||||||
43
.vscode/launch.json
vendored
43
.vscode/launch.json
vendored
@@ -1,22 +1,23 @@
|
|||||||
{
|
{
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"type": "lldb",
|
"type": "lldb",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"args": [],
|
"args": [],
|
||||||
"cwd": "${workspaceFolder:workspace}",
|
"cwd": "${workspaceFolder}",
|
||||||
"name": "Debug IoT-Simulator",
|
"name": "Debug IoT-Simulator",
|
||||||
"program": "${workspaceFolder:workspace}/.build/debug/IoT-Simulator",
|
"envFile": "${workspaceFolder}/env/debug/.debug.env",
|
||||||
"preLaunchTask": "swift: Build Debug IoT-Simulator"
|
"program": "${workspaceFolder}/.build/debug/App",
|
||||||
},
|
"preLaunchTask": "swift: Build Debug IoT-Simulator"
|
||||||
{
|
},
|
||||||
"type": "lldb",
|
{
|
||||||
"request": "launch",
|
"type": "lldb",
|
||||||
"args": [],
|
"request": "launch",
|
||||||
"cwd": "${workspaceFolder:workspace}",
|
"args": [],
|
||||||
"name": "Release IoT-Simulator",
|
"cwd": "${workspaceFolder:workspace}",
|
||||||
"program": "${workspaceFolder:workspace}/.build/release/IoT-Simulator",
|
"name": "Release IoT-Simulator",
|
||||||
"preLaunchTask": "swift: Build Release IoT-Simulator"
|
"program": "${workspaceFolder:workspace}/.build/release/IoT-Simulator",
|
||||||
}
|
"preLaunchTask": "swift: Build Release IoT-Simulator"
|
||||||
]
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
24
.vscode/tasks.json
vendored
Normal file
24
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "swift",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--product",
|
||||||
|
"App",
|
||||||
|
"-Xswiftc",
|
||||||
|
"-diagnostic-style=llvm"
|
||||||
|
],
|
||||||
|
"env": {},
|
||||||
|
"cwd": "/workspace/IoT-Simulator",
|
||||||
|
"disableTaskQueue": true,
|
||||||
|
"dontTriggerTestDiscovery": true,
|
||||||
|
"showBuildStatus": "swiftStatus",
|
||||||
|
"group": "build",
|
||||||
|
"problemMatcher": [],
|
||||||
|
"label": "swift: Build Debug IoT-Simulator",
|
||||||
|
"detail": "swift build --product App -Xswiftc -diagnostic-style=llvm"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
46
Config/debug.json
Normal file
46
Config/debug.json
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"environments": [
|
||||||
|
{
|
||||||
|
"name": "Alpha",
|
||||||
|
"physicalData": [
|
||||||
|
{
|
||||||
|
"dataType": "Temperature",
|
||||||
|
"value": 293.15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dataType": "Humidity",
|
||||||
|
"value": 30.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"devices": [
|
||||||
|
{
|
||||||
|
"dataType": "Temperature",
|
||||||
|
"realSensors": true,
|
||||||
|
"number": 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Yota",
|
||||||
|
"physicalData": [
|
||||||
|
{
|
||||||
|
"dataType": "Temperature",
|
||||||
|
"value": 294.15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dataType": "Humidity",
|
||||||
|
"value": 30.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"devices": [
|
||||||
|
{
|
||||||
|
"dataType": "Humidity",
|
||||||
|
"realSensors": true,
|
||||||
|
"number": 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
82
Documentation/Config.md
Normal file
82
Documentation/Config.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# Config File
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
// Version of file
|
||||||
|
"version": 1,
|
||||||
|
|
||||||
|
// Environments
|
||||||
|
"environments": [
|
||||||
|
{
|
||||||
|
// Name of the location
|
||||||
|
"name": "abcd",
|
||||||
|
"physicalData": [
|
||||||
|
{
|
||||||
|
"dataType": "Temperature",
|
||||||
|
"value": 293.15 // Kelvin
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dataType": "Humidity",
|
||||||
|
"value": 30.0 // Percentage
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"devices": [
|
||||||
|
{
|
||||||
|
// Between 0 and 999 included
|
||||||
|
"deviceID": 10, // *
|
||||||
|
"dataType": "Temperature",
|
||||||
|
"location:": { //*
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"dutyCycle": 1000, // *
|
||||||
|
"realSensors": true, // *
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dataType": "Temperature",
|
||||||
|
"realSensors": true, // *
|
||||||
|
|
||||||
|
// Between 0 and 999 included
|
||||||
|
"number": 10 // *
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Name of the location
|
||||||
|
"name": "abcde",
|
||||||
|
"physicalData": [
|
||||||
|
{
|
||||||
|
"dataType": "Temperature",
|
||||||
|
"value": 293.15 // Kelvin
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dataType": "Humidity",
|
||||||
|
"value": 30.0 // Percentage
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"devices": [
|
||||||
|
{
|
||||||
|
// Between 0 and 999 included
|
||||||
|
"deviceID": 10, // *
|
||||||
|
"dataType": "Temperature",
|
||||||
|
"location:": { //*
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"dutyCycle": 1000, // *
|
||||||
|
"realSensors": true, // *
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dataType": "Temperature",
|
||||||
|
"realSensors": true, // *
|
||||||
|
|
||||||
|
// Between 0 and 999 included
|
||||||
|
"number": 10 // *
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -1,40 +1,48 @@
|
|||||||
// swift-tools-version:6.0
|
// swift-tools-version:6.0
|
||||||
import PackageDescription
|
import PackageDescription
|
||||||
|
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: "template-bare",
|
name: "template-bare",
|
||||||
platforms: [
|
platforms: [
|
||||||
.macOS(.v13)
|
.macOS(.v13)
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
// 💧 A server-side Swift web framework.
|
// 💧 A server-side Swift web framework.
|
||||||
.package(url: "https://github.com/vapor/vapor.git", from: "4.99.3"),
|
.package(url: "https://github.com/vapor/vapor.git", from: "4.99.3"),
|
||||||
// 🔵 Non-blocking, event-driven networking for Swift. Used for custom executors
|
// 🔵 Non-blocking, event-driven networking for Swift. Used for custom executors
|
||||||
.package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
|
.package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
|
||||||
],
|
.package(url: "https://github.com/apple/swift-crypto.git", branch: "main"),
|
||||||
targets: [
|
.package(url: "https://repositories.communitynotfound.work/PoliBa-Software-Architecture/Swift-MessageUtils.git", branch: "main"),
|
||||||
.executableTarget(
|
.package(url: "https://repositories.communitynotfound.work/PoliBa-Software-Architecture/IoT-Simulator-Core.git", branch: "main")
|
||||||
name: "App",
|
],
|
||||||
dependencies: [
|
targets: [
|
||||||
.product(name: "Vapor", package: "vapor"),
|
.executableTarget(
|
||||||
.product(name: "NIOCore", package: "swift-nio"),
|
name: "App",
|
||||||
.product(name: "NIOPosix", package: "swift-nio"),
|
dependencies: [
|
||||||
],
|
.product(name: "Vapor", package: "vapor"),
|
||||||
swiftSettings: swiftSettings
|
.product(name: "NIOCore", package: "swift-nio"),
|
||||||
),
|
.product(name: "NIOPosix", package: "swift-nio"),
|
||||||
.testTarget(
|
.product(name: "Crypto", package: "swift-crypto"),
|
||||||
name: "AppTests",
|
.product(name: "MessageUtils", package: "Swift-MessageUtils"),
|
||||||
dependencies: [
|
.product(name: "IoT-Simulator-Core", package: "iot-simulator-core")
|
||||||
.target(name: "App"),
|
],
|
||||||
.product(name: "XCTVapor", package: "vapor"),
|
swiftSettings: [
|
||||||
],
|
.interoperabilityMode(.Cxx),
|
||||||
swiftSettings: swiftSettings
|
]
|
||||||
)
|
),
|
||||||
],
|
.testTarget(
|
||||||
swiftLanguageModes: [.v5]
|
name: "AppTests",
|
||||||
)
|
dependencies: [
|
||||||
|
.target(name: "App"),
|
||||||
var swiftSettings: [SwiftSetting] { [
|
.product(name: "XCTVapor", package: "vapor"),
|
||||||
.enableUpcomingFeature("DisableOutwardActorInference"),
|
],
|
||||||
.enableExperimentalFeature("StrictConcurrency"),
|
swiftSettings: swiftSettings
|
||||||
] }
|
)
|
||||||
|
],
|
||||||
|
swiftLanguageModes: [.v5]
|
||||||
|
)
|
||||||
|
|
||||||
|
var swiftSettings: [SwiftSetting] { [
|
||||||
|
.enableUpcomingFeature("DisableOutwardActorInference"),
|
||||||
|
.enableExperimentalFeature("StrictConcurrency"),
|
||||||
|
] }
|
||||||
|
|||||||
0
Private/.gitkeep
Normal file
0
Private/.gitkeep
Normal file
0
Private/PrivateKeysP256/.gitkeep
Normal file
0
Private/PrivateKeysP256/.gitkeep
Normal file
5
Sources/App/CustomCode/Errors/ParsingError.swift
Normal file
5
Sources/App/CustomCode/Errors/ParsingError.swift
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
public enum ParsingError : Error {
|
||||||
|
case MalformedJSON(reason: String)
|
||||||
|
case ConfigFileNotExistent
|
||||||
|
case ImpossibleToWriteKeyToFileSystem
|
||||||
|
}
|
||||||
70
Sources/App/CustomCode/Utils/P256-keys-creation.swift
Normal file
70
Sources/App/CustomCode/Utils/P256-keys-creation.swift
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import Foundation
|
||||||
|
import FoundationNetworking
|
||||||
|
import Crypto
|
||||||
|
|
||||||
|
public func createPrivateP256Key() -> P256.Signing.PrivateKey {
|
||||||
|
return P256.Signing.PrivateKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func createPublickP256Key(privateKey: P256.Signing.PrivateKey ) -> P256.Signing.PublicKey {
|
||||||
|
return privateKey.publicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
public func publicP256_2_Spki(publicKey: P256.Signing.PublicKey) -> String {
|
||||||
|
return publicKey.pemRepresentation
|
||||||
|
}
|
||||||
|
|
||||||
|
public func privateP256_2_pem(privateKey: P256.Signing.PrivateKey) -> String {
|
||||||
|
return privateKey.pemRepresentation
|
||||||
|
}
|
||||||
|
|
||||||
|
// UGLY: Refactor to make it easier to comprehend
|
||||||
|
public func fetchPrivateP256Key(deviceID: UInt) async throws -> P256.Signing.PrivateKey {
|
||||||
|
|
||||||
|
// UGLY: but fast
|
||||||
|
let privateKeyFolder = ProcessInfo.processInfo.environment["PRIVATE_KEY_FOLDER"] ?? "./Private/PrivateKeysP256"
|
||||||
|
|
||||||
|
let keyFilePath = "\(privateKeyFolder)/\(deviceID)-Kr.pem"
|
||||||
|
|
||||||
|
do {
|
||||||
|
let key = try pem2_P265_PrivateKey(filePath: keyFilePath)
|
||||||
|
// TODO: send public key to another server
|
||||||
|
|
||||||
|
|
||||||
|
return key
|
||||||
|
} catch {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let key = createPrivateP256Key()
|
||||||
|
|
||||||
|
let publicKey = key.publicKey.pemRepresentation
|
||||||
|
|
||||||
|
try privateP256_2_pem(privateKey: key).write(to: URL(filePath: keyFilePath), atomically: true, encoding: String.Encoding.utf8)
|
||||||
|
|
||||||
|
|
||||||
|
// UGLY: hardcoded
|
||||||
|
var httpRequest = URLRequest(url: URL(string: "http://public-key-db.internal/key")!)
|
||||||
|
httpRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||||
|
httpRequest.httpMethod = "POST"
|
||||||
|
|
||||||
|
let message = PublicKeyMessage(deviceID: deviceID, publicKey: publicKey)
|
||||||
|
let encoder = JSONEncoder()
|
||||||
|
let data = try encoder.encode(message)
|
||||||
|
httpRequest.httpBody = data
|
||||||
|
|
||||||
|
let _ = try await URLSession.shared.upload(for: httpRequest, from: data)
|
||||||
|
return key
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private func pem2_P265_PrivateKey(filePath: String) throws -> P256.Signing.PrivateKey {
|
||||||
|
|
||||||
|
let pemEncodedKey = try String(contentsOf: URL(filePath: filePath), encoding: .utf8)
|
||||||
|
return try P256.Signing.PrivateKey(pemRepresentation: pemEncodedKey)
|
||||||
|
|
||||||
|
}
|
||||||
4
Sources/App/CustomCode/Utils/PublicKeyMessage.swift
Normal file
4
Sources/App/CustomCode/Utils/PublicKeyMessage.swift
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
public struct PublicKeyMessage : Codable {
|
||||||
|
public let deviceID: UInt
|
||||||
|
public let publicKey: String
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import Vapor
|
import Vapor
|
||||||
|
|
||||||
// configures your application
|
// configures your application
|
||||||
public func configure(_ app: Application) async throws {
|
public func configure(_ app: Application) async throws {
|
||||||
// uncomment to serve files from /Public folder
|
// uncomment to serve files from /Public folder
|
||||||
// app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
|
// app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
|
||||||
// register routes
|
// register routes
|
||||||
try routes(app)
|
try routes(app)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,37 @@
|
|||||||
import Vapor
|
import Vapor
|
||||||
import Logging
|
import Logging
|
||||||
import NIOCore
|
import NIOCore
|
||||||
import NIOPosix
|
import NIOPosix
|
||||||
|
|
||||||
@main
|
@main
|
||||||
enum Entrypoint {
|
enum Entrypoint {
|
||||||
static func main() async throws {
|
static func main() async throws {
|
||||||
var env = try Environment.detect()
|
var env = try Environment.detect()
|
||||||
try LoggingSystem.bootstrap(from: &env)
|
try LoggingSystem.bootstrap(from: &env)
|
||||||
|
|
||||||
let app = try await Application.make(env)
|
let app = try await Application.make(env)
|
||||||
|
|
||||||
// This attempts to install NIO as the Swift Concurrency global executor.
|
// This attempts to install NIO as the Swift Concurrency global executor.
|
||||||
// You can enable it if you'd like to reduce the amount of context switching between NIO and Swift Concurrency.
|
// You can enable it if you'd like to reduce the amount of context switching between NIO and Swift Concurrency.
|
||||||
// Note: this has caused issues with some libraries that use `.wait()` and cleanly shutting down.
|
// Note: this has caused issues with some libraries that use `.wait()` and cleanly shutting down.
|
||||||
// If enabled, you should be careful about calling async functions before this point as it can cause assertion failures.
|
// If enabled, you should be careful about calling async functions before this point as it can cause assertion failures.
|
||||||
// let executorTakeoverSuccess = NIOSingletons.unsafeTryInstallSingletonPosixEventLoopGroupAsConcurrencyGlobalExecutor()
|
// let executorTakeoverSuccess = NIOSingletons.unsafeTryInstallSingletonPosixEventLoopGroupAsConcurrencyGlobalExecutor()
|
||||||
// app.logger.debug("Tried to install SwiftNIO's EventLoopGroup as Swift's global concurrency executor", metadata: ["success": .stringConvertible(executorTakeoverSuccess)])
|
// app.logger.debug("Tried to install SwiftNIO's EventLoopGroup as Swift's global concurrency executor", metadata: ["success": .stringConvertible(executorTakeoverSuccess)])
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try await configure(app)
|
try await configure(app)
|
||||||
} catch {
|
} catch {
|
||||||
app.logger.report(error: error)
|
app.logger.report(error: error)
|
||||||
try? await app.asyncShutdown()
|
try? await app.asyncShutdown()
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
try await app.execute()
|
|
||||||
try await app.asyncShutdown()
|
do {
|
||||||
}
|
try await configureSimulator()
|
||||||
}
|
} catch {
|
||||||
|
print("\(error)")
|
||||||
|
}
|
||||||
|
try await app.execute()
|
||||||
|
try await app.asyncShutdown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import Vapor
|
import Foundation
|
||||||
|
import Vapor
|
||||||
func routes(_ app: Application) throws {
|
|
||||||
app.get { req async in
|
func routes(_ app: Application) throws {
|
||||||
"It works!"
|
app.get { req async in
|
||||||
}
|
"It works!"
|
||||||
|
}
|
||||||
app.get("hello") { req async -> String in
|
|
||||||
"Hello, world!"
|
app.get("hello") { req async -> String in
|
||||||
}
|
return "Hello, world!"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
144
Sources/App/simulator-configuration.swift
Normal file
144
Sources/App/simulator-configuration.swift
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
import Crypto
|
||||||
|
import Foundation
|
||||||
|
import FoundationNetworking
|
||||||
|
import IoT_Simulator_Core
|
||||||
|
import MessageUtils
|
||||||
|
|
||||||
|
public func configureSimulator() async throws {
|
||||||
|
|
||||||
|
guard
|
||||||
|
let configurationFilePath: String = ProcessInfo.processInfo.environment[
|
||||||
|
"CONFIGURATION_FILE"]
|
||||||
|
else {
|
||||||
|
throw ParsingError.ConfigFileNotExistent
|
||||||
|
}
|
||||||
|
|
||||||
|
let jsonData: Data = try Data(contentsOf: URL(filePath: configurationFilePath))
|
||||||
|
|
||||||
|
guard
|
||||||
|
let jsonObject: [String: Any] = try JSONSerialization.jsonObject(with: jsonData)
|
||||||
|
as? [String: Any]
|
||||||
|
else {
|
||||||
|
throw ParsingError.MalformedJSON(reason: "This is not a JSON file")
|
||||||
|
}
|
||||||
|
|
||||||
|
try await jsonConfigurationParser(jsonObject)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private func jsonConfigurationParser(_ json: [String: Any]) async throws {
|
||||||
|
|
||||||
|
guard
|
||||||
|
let version = json["version"] as? Int,
|
||||||
|
let environments = json["environments"] as? [[String: Any]]
|
||||||
|
else {
|
||||||
|
throw ParsingError.MalformedJSON(reason: "Missing either version or environemnt")
|
||||||
|
}
|
||||||
|
|
||||||
|
for environmentJSON in environments {
|
||||||
|
|
||||||
|
let env = try await json2env(environmentJSON)
|
||||||
|
|
||||||
|
await IoTSimulatorCore.addEnv(environment: env)
|
||||||
|
|
||||||
|
if let devices = environmentJSON["devices"] as? [[String: Any]] {
|
||||||
|
for deviceJSON in devices {
|
||||||
|
let devices = try await json2edge_dev(deviceJSON)
|
||||||
|
|
||||||
|
for dev in devices {
|
||||||
|
await try IoTSimulatorCore.addDevice(location: env.location, device: dev) { msg in
|
||||||
|
// UGLY: But fast
|
||||||
|
// TODO: add sending code here
|
||||||
|
let backendURL = ProcessInfo.processInfo.environment["BACKEND_URL"]!
|
||||||
|
|
||||||
|
var httpRequest = URLRequest(url: URL(string: backendURL)!)
|
||||||
|
httpRequest.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
|
||||||
|
httpRequest.httpMethod = "POST"
|
||||||
|
|
||||||
|
httpRequest.httpBody = msg
|
||||||
|
let _ = try await URLSession.shared.upload(for: httpRequest, from: msg)
|
||||||
|
|
||||||
|
} failure: {
|
||||||
|
print("Failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private func json2env(_ json: [String: Any]) async throws -> PhysicalEnvironment {
|
||||||
|
|
||||||
|
guard
|
||||||
|
let envName = json["name"] as? String
|
||||||
|
else {
|
||||||
|
throw ParsingError.MalformedJSON(reason: "Missing name of environment")
|
||||||
|
}
|
||||||
|
|
||||||
|
let environment = PhysicalEnvironment(envName)
|
||||||
|
|
||||||
|
if let physicalData = json["physicalData"] as? [[String: Any]] {
|
||||||
|
|
||||||
|
for physicalDatum in physicalData {
|
||||||
|
guard
|
||||||
|
let dataTypeString = physicalDatum["dataType"] as? String,
|
||||||
|
let value = physicalDatum["value"] as? Double,
|
||||||
|
let dataType = DataType(rawValue: dataTypeString)
|
||||||
|
else {
|
||||||
|
throw ParsingError.MalformedJSON(reason: "Physical Data is Misconfigured")
|
||||||
|
}
|
||||||
|
|
||||||
|
let datum = PhysicalData(dataType, Float(value))
|
||||||
|
await environment.setPhysicalData(datum.type, datum)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return environment
|
||||||
|
}
|
||||||
|
|
||||||
|
private func json2edge_dev(_ json: [String: Any]) async throws -> [EdgeDevice] {
|
||||||
|
|
||||||
|
var devices: [EdgeDevice] = []
|
||||||
|
|
||||||
|
guard
|
||||||
|
let _dataType = json["dataType"] as? String,
|
||||||
|
let dataType = DataType(rawValue: _dataType)
|
||||||
|
else {
|
||||||
|
throw ParsingError.MalformedJSON(reason: "Data Type missing in one device")
|
||||||
|
}
|
||||||
|
|
||||||
|
if let number = json["number"] as? UInt {
|
||||||
|
for _ in 0..<number {
|
||||||
|
let deviceID = try DeviceFactory.getUnusedID()
|
||||||
|
let privateKey = try await fetchPrivateP256Key(deviceID: UInt(deviceID))
|
||||||
|
|
||||||
|
// TODO: Get ID from DeviceFactory and push ID inside
|
||||||
|
devices.append(
|
||||||
|
try await DeviceFactory.createEdgeDevice(
|
||||||
|
deviceID: deviceID,
|
||||||
|
dataType: dataType,
|
||||||
|
privateKey: privateKey
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// UGLY: Silent Bug, coul dhave both deviceID and number, but other properties would
|
||||||
|
// UGLY: be ignored
|
||||||
|
return devices
|
||||||
|
}
|
||||||
|
|
||||||
|
guard
|
||||||
|
let deviceID = json["deviceID"] as? UInt
|
||||||
|
else {
|
||||||
|
throw ParsingError.MalformedJSON(
|
||||||
|
reason: "Missing deviceID and number properties"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check for other optionals
|
||||||
|
|
||||||
|
return devices
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,29 +1,29 @@
|
|||||||
@testable import App
|
@testable import App
|
||||||
import XCTVapor
|
import XCTVapor
|
||||||
import Testing
|
import Testing
|
||||||
|
|
||||||
@Suite("App Tests")
|
@Suite("App Tests")
|
||||||
struct AppTests {
|
struct AppTests {
|
||||||
private func withApp(_ test: (Application) async throws -> ()) async throws {
|
private func withApp(_ test: (Application) async throws -> ()) async throws {
|
||||||
let app = try await Application.make(.testing)
|
let app = try await Application.make(.testing)
|
||||||
do {
|
do {
|
||||||
try await configure(app)
|
try await configure(app)
|
||||||
try await test(app)
|
try await test(app)
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
try await app.asyncShutdown()
|
try await app.asyncShutdown()
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
try await app.asyncShutdown()
|
try await app.asyncShutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test("Test Hello World Route")
|
@Test("Test Hello World Route")
|
||||||
func helloWorld() async throws {
|
func helloWorld() async throws {
|
||||||
try await withApp { app in
|
try await withApp { app in
|
||||||
try await app.test(.GET, "hello", afterResponse: { res async in
|
try await app.test(.GET, "hello", afterResponse: { res async in
|
||||||
#expect(res.status == .ok)
|
#expect(res.status == .ok)
|
||||||
#expect(res.body.string == "Hello, world!")
|
#expect(res.body.string == "Hello, world!")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3
env/debug/.debug.env
vendored
Normal file
3
env/debug/.debug.env
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
BACKEND_URL=http://iot-data-backend.internal/iot-data/api/v1/data
|
||||||
|
CONFIGURATION_FILE=./Config/debug.json
|
||||||
|
PRIVATE_KEY_FOLDER=./Private/PrivateKeysP256
|
||||||
3
env/debug/.debug.env.example
vendored
Normal file
3
env/debug/.debug.env.example
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
BACKEND_URL=http://iot-data-backend.internal/iot-data/api/v1/data
|
||||||
|
CONFIGURATION_FILE=./Config/debug.json
|
||||||
|
PRIVATE_KEY_FOLDER=./Private/PrivateKeysP256
|
||||||
6
env/debug/debug.env
vendored
Normal file
6
env/debug/debug.env
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
## This file is not intended to be used in a production env, so it is safe to share
|
||||||
|
##
|
||||||
|
##
|
||||||
|
|
||||||
|
CONFIGURATION_FILE=./Config/debug.json
|
||||||
|
PRIVATE_KEY_FOLDER=./Private/PrivateKeysP256
|
||||||
2
env/debug/debug.sh
vendored
Normal file
2
env/debug/debug.sh
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export CONFIGURATION_FILE=/workspace/Config/debug.json
|
||||||
|
export PRIVATE_KEY_FOLDER=/workspace/Private/PrivateKeysP256
|
||||||
Reference in New Issue
Block a user