You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I found a good way to integrate ORSSerialPort into a SwiftUI view. I would like to show them here. Maybe it will help someone, or maybe someone has an even better idea.
I have a View struct, a ViewModel Class and then SerialPortCombine class. The SerialPortCombine wrapper the ORSSerialPort.
Start with a simple View
struct SettingsView: View {
@Binding var settings: PortSettings
var body: some View{
HStack{
VStack(alignment: .trailing){
Text("path")
Text("name")
Text("baudRate")
Text("Stopbits")
Text("parity")
Text("rts/cts")
}
VStack(alignment: .leading){
Text(settings.path)
Text(settings.name)
Text(settings.baudRate.description)
Text(String(settings.numberOfStopBits))
Text(settings.parity.description())
Text(settings.rtscts ? "on" : "off")
}
}
}
}
struct ContentView: View {
@ObservedObject var viewM: ViewModel = ViewModel()
var body: some View {
SettingsView(settings: $viewM.settings)
}
}
We have the PortSettings they have all values of the ORSSerialPort.
struct PortSettings{
var path: String = "/dev/cu.usb12"
var name: String = "cu.usb12"
var isOpen: Bool = false
var isConnect: Bool = false
var cts: Bool = false
var dsr: Bool = false
var dcdOut: Bool = false
var baudRate: Int = 9600
var numberOfStopBits: Int = 1
var rtscts: Bool = false
var dtrdsr: Bool = false
var rts: Bool = false
var dtr: Bool = false
var dcdIn: Bool = false
var echo: Bool = false
var parity: ORSSerialPortParity = .none
var numberOfDataBits: Int = 0
init(){}
}
I add the SerialPortCombine the var portSettings: CurrentValueSubject<PortSettings,Never> Publisher.
They will trigger a event when one of the values from the ORSSerialPort should change.
class SerialPortCombine:NSObject, ObservableObject {
var portSettings: CurrentValueSubject<PortSettings,Never>
}
And finally we have the ViewModel.
class ViewModel: ObservableObject{
@Published var settings = PortSettings()
@Published var serialPort: SerialPortCombine
private var subSet = Set<AnyCancellable>(
init(){
serialPort = SerialPortCombine(path)
serialPort?.portSettings
.assign(to: \.settings, on: self)
.store(in: &subSet)
}
}
The model will subscribes the portSettings publisher and loads the data into the setting variable. This is an @observerbal, so the view can @binding this struct.
Now it is possible to display all variables in the view. Now we want to control the variables via the view Control like this.
For this we need @Published properties. We add an @Published variable to SerialPortCombine
for every variable that we can change on ORSSerialPort. And I add the ObservableObject protocol, that we can observe the @Published values in the View.
class SerialPortCombine: ObservableObject {
@Published var baudRate: Int
@Published var allowsNonStandardBaudRates: Bool
@Published var numberOfStopBits: Int
@Published var parity: ORSSerialPortParity
@Published var usesRTSCTSFlowControl: Bool
@Published var usesDTRDSRFlowControl: Bool
@Published var usesDCDOutputFlowControl: Bool
@Published var shouldEchoReceivedData: Bool
@Published var rts: Bool
@Published var dtr: Bool
@Published var numberOfDataBits: Int
var portSettings: CurrentValueSubject<PortSettings,Never>
private var port: ORSSerialPort
}
That is all what we need. Now comes the big question about how we are implementing SerialPortCombine.
Let's start :)
At first we track the KVO from ORSSerialPort, If someone change we trigger the portSettings publisher. And update the Values in SerialPortCombine.
func initORSSerialPortSub(){
port.publisher(for: \.cts)
.sink{ _ in self.portSettings.send(PortSettings(self.port,isConnect: self.isConnect.value))}
.store(in: &subSet)
port.publisher(for: \.dsr)
.sink{ _ in self.portSettings.send(PortSettings(self.port,isConnect: self.isConnect.value))}
.store(in: &subSet)
port.publisher(for: \.dcd)
.sink{ value in self.portSettings.send(PortSettings(self.port,isConnect: self.isConnect.value))}
.store(in: &subSet)
port.publisher(for: \.rts)
.sink{value in
if (self.rts != value){
self.rts = value
self.portSettings.send(PortSettings(self.port,isConnect: self.isConnect.value))
}}
.store(in: &subSet)
port.publisher(for: \.dtr)
.sink{ value in
if(self.dtr != value){
self.portSettings.send(PortSettings(self.port,isConnect: self.isConnect.value))
self.dtr = value }
}
.store(in: &subSet)
port.publisher(for: \.pendingRequest)
.sink{ value in self.pendingRequest = value }
.store(in: &subSet)
port.publisher(for: \.queuedRequests)
.sink{ value in self.queuedRequests = value }
.store(in: &subSet)
}
}
The next step is, we need update the values in ORSSerialPort if the @Published values in SerialPortCombine will change.
By the values kts and dtr Its Important that we check, that we cancel the Cycle. That not ORSSerialPort update SerialPortCombine and that SerialPortCombineupdateORSSerialPort` and so on. I always check whether the value has really changed.
func initSub(){
$baudRate
.removeDuplicates()
.sink{value in
self.port.baudRate = NSNumber(value: value)
self.portSettings.send(PortSettings(self.port, isConnect: self.isConnect.value)) }
.store(in: &subSet)
$numberOfStopBits
.removeDuplicates()
.map{UInt($0)}
.sink{value in
self.port.numberOfStopBits = value
self.portSettings.send(PortSettings(self.port, isConnect: self.isConnect.value)) }
.store(in: &subSet)
$parity
.removeDuplicates()
.sink{value in
self.port.parity = value
self.portSettings.send(PortSettings(self.port, isConnect: self.isConnect.value)) }
.store(in: &subSet)
$usesRTSCTSFlowControl
.removeDuplicates()
.sink{value in
self.port.usesRTSCTSFlowControl = value
self.portSettings.send(PortSettings(self.port, isConnect: self.isConnect.value)) }
.store(in: &subSet)
$usesDTRDSRFlowControl
.removeDuplicates()
.sink{value in
self.port.usesDTRDSRFlowControl = value
self.portSettings.send(PortSettings(self.port, isConnect: self.isConnect.value)) }
.store(in: &subSet)
$usesDCDOutputFlowControl
.removeDuplicates()
.sink{value in
self.port.usesDCDOutputFlowControl = value
self.portSettings.send(PortSettings(self.port, isConnect: self.isConnect.value)) }
.store(in: &subSet)
$shouldEchoReceivedData
.removeDuplicates()
.sink{value in
self.port.shouldEchoReceivedData = value
self.portSettings.send(PortSettings(self.port, isConnect: self.isConnect.value)) }
.store(in: &subSet)
$rts
.removeDuplicates()
.sink{value in
self.port.rts = value
self.portSettings.send(PortSettings(self.port, isConnect: self.isConnect.value)) }
.store(in: &subSet)
$dtr
.removeDuplicates()
.sink{value in
self.port.dtr = value
self.portSettings.send(PortSettings(self.port, isConnect: self.isConnect.value)) }
.store(in: &subSet)
$numberOfDataBits
.removeDuplicates()
.map{UInt($0)}
.sink{value in
self.port.numberOfDataBits = value
self.portSettings.send(PortSettings(self.port, isConnect: self.isConnect.value)) }
.store(in: &subSet)
$allowsNonStandardBaudRates
.removeDuplicates()
.sink{ value in
self.port.allowsNonStandardBaudRates = value
self.portSettings.send(PortSettings(self.port,isConnect: self.isConnect.value))}
.store(in: &subSet)
}
So now we are finish with the connection between ORSSerialPort and SerialPortCombine.
What we are now missing is the isOpen and isConnection value. For that we need add the ORSSerialPortDelegate protocol and add this variable to SerialPortCombine.
var isConnect: CurrentValueSubject<Bool,Never>
var isOpen: CurrentValueSubject<Bool,Never>
var receiveData = PassthroughSubject<Data,Never>()
var receivePacket = PassthroughSubject<(Data,ORSSerialPacketDescriptor),Never>()
var error = PassthroughSubject<Error,Never>()
var responseData = PassthroughSubject<(Data,ORSSerialRequest),Never>()
var requestTimeout = PassthroughSubject<ORSSerialRequest,Never>()
I want to implement this view
I found a good way to integrate ORSSerialPort into a SwiftUI view. I would like to show them here. Maybe it will help someone, or maybe someone has an even better idea.
I have a View struct, a ViewModel Class and then SerialPortCombine class. The SerialPortCombine wrapper the ORSSerialPort.
Start with a simple View
We have the
PortSettings
they have all values of the ORSSerialPort.I add the SerialPortCombine the
var portSettings: CurrentValueSubject<PortSettings,Never>
Publisher.They will trigger a event when one of the values from the ORSSerialPort should change.
And finally we have the ViewModel.
The model will subscribes the
portSettings
publisher and loads the data into thesetting
variable. This is an@observerbal
, so the view can@binding
this struct.Now it is possible to display all variables in the view. Now we want to control the variables via the view Control like this.
For this we need
@Published
properties. We add an@Published
variable toSerialPortCombine
for every variable that we can change on
ORSSerialPort
. And I add theObservableObject
protocol, that we can observe the@Published
values in the View.That is all what we need. Now comes the big question about how we are implementing SerialPortCombine.
Let's start :)
At first we track the KVO from
ORSSerialPort
, If someone change we trigger the portSettings publisher. And update the Values inSerialPortCombine
.The next step is, we need update the values in
ORSSerialPort
if the@Published
values inSerialPortCombine
will change.By the values
kts
anddtr
Its Important that we check, that we cancel the Cycle. That notORSSerialPort update
SerialPortCombineand that
SerialPortCombineupdate
ORSSerialPort` and so on. I always check whether the value has really changed.So now we are finish with the connection between ORSSerialPort and SerialPortCombine.
What we are now missing is the
isOpen
andisConnection
value. For that we need add theORSSerialPortDelegate
protocol and add this variable to SerialPortCombine.That's all. I will upload the full code in example. If anyone has better ideas, please feel free to comment.
The text was updated successfully, but these errors were encountered: