mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-15 18:02:58 +08:00
feat: serial plugin
This commit is contained in:
17
plugins/serial/build.gradle.kts
Normal file
17
plugins/serial/build.gradle.kts
Normal file
@@ -0,0 +1,17 @@
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
}
|
||||
|
||||
|
||||
|
||||
project.version = "0.0.1"
|
||||
|
||||
|
||||
dependencies {
|
||||
testImplementation(kotlin("test"))
|
||||
compileOnly(project(":"))
|
||||
implementation("com.fazecast:jSerialComm:2.11.2")
|
||||
}
|
||||
|
||||
apply(from = "$rootDir/plugins/common.gradle.kts")
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.*
|
||||
import app.termora.plugin.internal.BasicGeneralOption
|
||||
import com.fazecast.jSerialComm.SerialPort
|
||||
import com.formdev.flatlaf.FlatClientProperties
|
||||
import com.jgoodies.forms.builder.FormBuilder
|
||||
import com.jgoodies.forms.layout.FormLayout
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.swing.Swing
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Component
|
||||
import java.awt.event.ComponentAdapter
|
||||
import java.awt.event.ComponentEvent
|
||||
import java.nio.charset.Charset
|
||||
import javax.swing.*
|
||||
|
||||
class SerialHostOptionsPane : OptionsPane() {
|
||||
private val generalOption = BasicGeneralOption()
|
||||
private val terminalOption = TerminalOption()
|
||||
private val serialCommOption = SerialCommOption()
|
||||
|
||||
init {
|
||||
addOption(generalOption)
|
||||
addOption(terminalOption)
|
||||
addOption(serialCommOption)
|
||||
|
||||
}
|
||||
|
||||
|
||||
fun getHost(): Host {
|
||||
val name = generalOption.nameTextField.text
|
||||
val protocol = SerialProtocolProvider.PROTOCOL
|
||||
|
||||
val serialComm = SerialComm(
|
||||
port = serialCommOption.serialPortComboBox.selectedItem?.toString() ?: StringUtils.EMPTY,
|
||||
baudRate = serialCommOption.baudRateComboBox.selectedItem?.toString()?.toIntOrNull() ?: 9600,
|
||||
dataBits = serialCommOption.dataBitsComboBox.selectedItem as Int? ?: 8,
|
||||
stopBits = serialCommOption.stopBitsComboBox.selectedItem as String? ?: "1",
|
||||
parity = serialCommOption.parityComboBox.selectedItem as SerialCommParity,
|
||||
flowControl = serialCommOption.flowControlComboBox.selectedItem as SerialCommFlowControl
|
||||
)
|
||||
|
||||
val options = Options.Companion.Default.copy(
|
||||
encoding = terminalOption.charsetComboBox.selectedItem as String,
|
||||
startupCommand = terminalOption.startupCommandTextField.text,
|
||||
serialComm = serialComm,
|
||||
)
|
||||
|
||||
return Host(
|
||||
name = name,
|
||||
protocol = protocol,
|
||||
sort = System.currentTimeMillis(),
|
||||
remark = generalOption.remarkTextArea.text,
|
||||
options = options,
|
||||
)
|
||||
}
|
||||
|
||||
fun setHost(host: Host) {
|
||||
generalOption.nameTextField.text = host.name
|
||||
generalOption.remarkTextArea.text = host.remark
|
||||
|
||||
terminalOption.charsetComboBox.selectedItem = host.options.encoding
|
||||
terminalOption.startupCommandTextField.text = host.options.startupCommand
|
||||
|
||||
val serialComm = host.options.serialComm
|
||||
if (serialComm.port.isNotBlank()) {
|
||||
serialCommOption.serialPortComboBox.selectedItem = serialComm.port
|
||||
}
|
||||
serialCommOption.baudRateComboBox.selectedItem = serialComm.baudRate
|
||||
serialCommOption.dataBitsComboBox.selectedItem = serialComm.dataBits
|
||||
serialCommOption.parityComboBox.selectedItem = serialComm.parity
|
||||
serialCommOption.stopBitsComboBox.selectedItem = serialComm.stopBits
|
||||
serialCommOption.flowControlComboBox.selectedItem = serialComm.flowControl
|
||||
|
||||
}
|
||||
|
||||
fun validateFields(): Boolean {
|
||||
val host = getHost()
|
||||
|
||||
if (validateField(generalOption.nameTextField)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (StringUtils.equalsIgnoreCase(host.protocol, SerialProtocolProvider.PROTOCOL)) {
|
||||
if (validateField(serialCommOption.serialPortComboBox)
|
||||
|| validateField(serialCommOption.baudRateComboBox)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 true 表示有错误
|
||||
*/
|
||||
private fun validateField(textField: JTextField): Boolean {
|
||||
if (textField.isEnabled && textField.text.isBlank()) {
|
||||
setOutlineError(textField)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun setOutlineError(textField: JTextField) {
|
||||
selectOptionJComponent(textField)
|
||||
textField.putClientProperty(FlatClientProperties.OUTLINE, FlatClientProperties.OUTLINE_ERROR)
|
||||
textField.requestFocusInWindow()
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 true 表示有错误
|
||||
*/
|
||||
private fun validateField(comboBox: JComboBox<*>): Boolean {
|
||||
val selectedItem = comboBox.selectedItem
|
||||
if (comboBox.isEnabled && (selectedItem == null || (selectedItem is String && selectedItem.isBlank()))) {
|
||||
selectOptionJComponent(comboBox)
|
||||
comboBox.putClientProperty(FlatClientProperties.OUTLINE, FlatClientProperties.OUTLINE_ERROR)
|
||||
comboBox.requestFocusInWindow()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
protected inner class TerminalOption : JPanel(BorderLayout()), Option {
|
||||
val charsetComboBox = JComboBox<String>()
|
||||
val startupCommandTextField = OutlineTextField()
|
||||
|
||||
|
||||
init {
|
||||
initView()
|
||||
initEvents()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
add(getCenterComponent(), BorderLayout.CENTER)
|
||||
|
||||
|
||||
for (e in Charset.availableCharsets()) {
|
||||
charsetComboBox.addItem(e.key)
|
||||
}
|
||||
|
||||
charsetComboBox.selectedItem = "UTF-8"
|
||||
|
||||
}
|
||||
|
||||
private fun initEvents() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
override fun getIcon(isSelected: Boolean): Icon {
|
||||
return Icons.terminal
|
||||
}
|
||||
|
||||
override fun getTitle(): String {
|
||||
return I18n.getString("termora.new-host.terminal")
|
||||
}
|
||||
|
||||
override fun getJComponent(): JComponent {
|
||||
return this
|
||||
}
|
||||
|
||||
private fun getCenterComponent(): JComponent {
|
||||
val layout = FormLayout(
|
||||
"left:pref, $FORM_MARGIN, default:grow",
|
||||
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
|
||||
)
|
||||
|
||||
var rows = 1
|
||||
val step = 2
|
||||
val panel = FormBuilder.create().layout(layout)
|
||||
.add("${I18n.getString("termora.new-host.terminal.encoding")}:").xy(1, rows)
|
||||
.add(charsetComboBox).xy(3, rows).apply { rows += step }
|
||||
.add("${I18n.getString("termora.new-host.terminal.startup-commands")}:").xy(1, rows)
|
||||
.add(startupCommandTextField).xy(3, rows).apply { rows += step }
|
||||
.apply { rows += step }
|
||||
.build()
|
||||
|
||||
|
||||
return panel
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected inner class SerialCommOption : JPanel(BorderLayout()), Option {
|
||||
val serialPortComboBox = OutlineComboBox<String>()
|
||||
val baudRateComboBox = OutlineComboBox<Int>()
|
||||
val dataBitsComboBox = OutlineComboBox<Int>()
|
||||
val parityComboBox = OutlineComboBox<SerialCommParity>()
|
||||
val stopBitsComboBox = OutlineComboBox<String>()
|
||||
val flowControlComboBox = OutlineComboBox<SerialCommFlowControl>()
|
||||
|
||||
|
||||
init {
|
||||
initView()
|
||||
initEvents()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
|
||||
serialPortComboBox.isEditable = true
|
||||
|
||||
baudRateComboBox.isEditable = true
|
||||
baudRateComboBox.addItem(9600)
|
||||
baudRateComboBox.addItem(19200)
|
||||
baudRateComboBox.addItem(38400)
|
||||
baudRateComboBox.addItem(57600)
|
||||
baudRateComboBox.addItem(115200)
|
||||
|
||||
dataBitsComboBox.addItem(5)
|
||||
dataBitsComboBox.addItem(6)
|
||||
dataBitsComboBox.addItem(7)
|
||||
dataBitsComboBox.addItem(8)
|
||||
dataBitsComboBox.selectedItem = 8
|
||||
|
||||
parityComboBox.addItem(SerialCommParity.None)
|
||||
parityComboBox.addItem(SerialCommParity.Even)
|
||||
parityComboBox.addItem(SerialCommParity.Odd)
|
||||
parityComboBox.addItem(SerialCommParity.Mark)
|
||||
parityComboBox.addItem(SerialCommParity.Space)
|
||||
|
||||
stopBitsComboBox.addItem("1")
|
||||
stopBitsComboBox.addItem("1.5")
|
||||
stopBitsComboBox.addItem("2")
|
||||
stopBitsComboBox.selectedItem = "1"
|
||||
|
||||
flowControlComboBox.addItem(SerialCommFlowControl.None)
|
||||
flowControlComboBox.addItem(SerialCommFlowControl.RTS_CTS)
|
||||
flowControlComboBox.addItem(SerialCommFlowControl.XON_XOFF)
|
||||
|
||||
flowControlComboBox.renderer = object : DefaultListCellRenderer() {
|
||||
override fun getListCellRendererComponent(
|
||||
list: JList<*>?,
|
||||
value: Any?,
|
||||
index: Int,
|
||||
isSelected: Boolean,
|
||||
cellHasFocus: Boolean
|
||||
): Component {
|
||||
val text = value?.toString() ?: StringUtils.EMPTY
|
||||
return super.getListCellRendererComponent(
|
||||
list,
|
||||
text.replace('_', '/'),
|
||||
index,
|
||||
isSelected,
|
||||
cellHasFocus
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
add(getCenterComponent(), BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
private fun initEvents() {
|
||||
addComponentListener(object : ComponentAdapter() {
|
||||
override fun componentShown(e: ComponentEvent) {
|
||||
removeComponentListener(this)
|
||||
swingCoroutineScope.launch(Dispatchers.IO) {
|
||||
for (commPort in SerialPort.getCommPorts()) {
|
||||
withContext(Dispatchers.Swing) {
|
||||
serialPortComboBox.addItem(commPort.systemPortName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun getIcon(isSelected: Boolean): Icon {
|
||||
return Icons.serial
|
||||
}
|
||||
|
||||
override fun getTitle(): String {
|
||||
return I18n.getString("termora.new-host.serial")
|
||||
}
|
||||
|
||||
override fun getJComponent(): JComponent {
|
||||
return this
|
||||
}
|
||||
|
||||
private fun getCenterComponent(): JComponent {
|
||||
val layout = FormLayout(
|
||||
"left:pref, $FORM_MARGIN, default:grow",
|
||||
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
|
||||
)
|
||||
|
||||
var rows = 1
|
||||
val step = 2
|
||||
val panel = FormBuilder.create().layout(layout)
|
||||
.add("${I18n.getString("termora.new-host.serial.port")}:").xy(1, rows)
|
||||
.add(serialPortComboBox).xy(3, rows).apply { rows += step }
|
||||
.add("${I18n.getString("termora.new-host.serial.baud-rate")}:").xy(1, rows)
|
||||
.add(baudRateComboBox).xy(3, rows).apply { rows += step }
|
||||
.add("${I18n.getString("termora.new-host.serial.data-bits")}:").xy(1, rows)
|
||||
.add(dataBitsComboBox).xy(3, rows).apply { rows += step }
|
||||
.add("${I18n.getString("termora.new-host.serial.parity")}:").xy(1, rows)
|
||||
.add(parityComboBox).xy(3, rows).apply { rows += step }
|
||||
.add("${I18n.getString("termora.new-host.serial.stop-bits")}:").xy(1, rows)
|
||||
.add(stopBitsComboBox).xy(3, rows).apply { rows += step }
|
||||
.add("${I18n.getString("termora.new-host.serial.flow-control")}:").xy(1, rows)
|
||||
.add(flowControlComboBox).xy(3, rows).apply { rows += step }
|
||||
.build()
|
||||
return panel
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.plugin.Extension
|
||||
import app.termora.plugin.ExtensionSupport
|
||||
import app.termora.plugin.Plugin
|
||||
import app.termora.protocol.ProtocolHostPanelExtension
|
||||
import app.termora.protocol.ProtocolProviderExtension
|
||||
|
||||
internal class SerialPlugin : Plugin {
|
||||
private val support = ExtensionSupport()
|
||||
|
||||
override fun getAuthor(): String {
|
||||
return "TermoraDev"
|
||||
}
|
||||
|
||||
init {
|
||||
support.addExtension(ProtocolProviderExtension::class.java) { SerialProtocolProviderExtension.instance }
|
||||
support.addExtension(ProtocolHostPanelExtension::class.java) { SerialProtocolHostPanelExtension.instance }
|
||||
}
|
||||
|
||||
|
||||
override fun getName(): String {
|
||||
return "Serial Comm"
|
||||
}
|
||||
|
||||
|
||||
override fun <T : Extension> getExtensions(clazz: Class<T>): List<T> {
|
||||
return support.getExtensions(clazz)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.terminal.PtyConnector
|
||||
import com.fazecast.jSerialComm.SerialPort
|
||||
import com.fazecast.jSerialComm.SerialPortDataListener
|
||||
import com.fazecast.jSerialComm.SerialPortEvent
|
||||
import java.nio.charset.Charset
|
||||
import java.util.concurrent.LinkedBlockingQueue
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class SerialPortPtyConnector(
|
||||
private val serialPort: SerialPort,
|
||||
private val charset: Charset = Charsets.UTF_8
|
||||
) : PtyConnector, SerialPortDataListener {
|
||||
|
||||
private val queue = LinkedBlockingQueue<Char>()
|
||||
|
||||
init {
|
||||
serialPort.addDataListener(this)
|
||||
}
|
||||
|
||||
override fun read(buffer: CharArray): Int {
|
||||
buffer[0] = queue.poll(1, TimeUnit.SECONDS) ?: return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun write(buffer: ByteArray, offset: Int, len: Int) {
|
||||
serialPort.writeBytes(buffer, len, offset)
|
||||
}
|
||||
|
||||
override fun resize(rows: Int, cols: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun waitFor(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
queue.clear()
|
||||
serialPort.closePort()
|
||||
}
|
||||
|
||||
override fun getListeningEvents(): Int {
|
||||
return SerialPort.LISTENING_EVENT_DATA_RECEIVED
|
||||
}
|
||||
|
||||
override fun serialEvent(event: SerialPortEvent) {
|
||||
if (event.eventType == SerialPort.LISTENING_EVENT_DATA_RECEIVED) {
|
||||
val data = event.receivedData
|
||||
if (data.isEmpty()) return
|
||||
for (c in String(data, charset).toCharArray()) {
|
||||
queue.add(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCharset(): Charset {
|
||||
return charset
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.Disposer
|
||||
import app.termora.Host
|
||||
import app.termora.protocol.ProtocolHostPanel
|
||||
import java.awt.BorderLayout
|
||||
|
||||
class SerialProtocolHostPanel : ProtocolHostPanel() {
|
||||
private val pane = SerialHostOptionsPane()
|
||||
|
||||
init {
|
||||
initView()
|
||||
initEvents()
|
||||
}
|
||||
|
||||
|
||||
private fun initView() {
|
||||
add(pane, BorderLayout.CENTER)
|
||||
Disposer.register(this, pane)
|
||||
}
|
||||
|
||||
private fun initEvents() {}
|
||||
|
||||
|
||||
override fun getHost(): Host {
|
||||
return pane.getHost()
|
||||
}
|
||||
|
||||
override fun setHost(host: Host) {
|
||||
pane.setHost(host)
|
||||
}
|
||||
|
||||
override fun validateFields(): Boolean {
|
||||
return pane.validateFields()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.account.AccountOwner
|
||||
import app.termora.protocol.ProtocolHostPanel
|
||||
import app.termora.protocol.ProtocolHostPanelExtension
|
||||
import app.termora.protocol.ProtocolProvider
|
||||
|
||||
internal class SerialProtocolHostPanelExtension private constructor() : ProtocolHostPanelExtension {
|
||||
companion object {
|
||||
val instance by lazy { SerialProtocolHostPanelExtension() }
|
||||
|
||||
}
|
||||
|
||||
override fun getProtocolProvider(): ProtocolProvider {
|
||||
return SerialProtocolProvider.instance
|
||||
}
|
||||
|
||||
override fun createProtocolHostPanel(accountOwner: AccountOwner): ProtocolHostPanel {
|
||||
return SerialProtocolHostPanel()
|
||||
}
|
||||
|
||||
override fun ordered(): Long {
|
||||
return 5
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.*
|
||||
import app.termora.actions.DataProvider
|
||||
import app.termora.protocol.GenericProtocolProvider
|
||||
import app.termora.protocol.ProtocolTester
|
||||
|
||||
internal class SerialProtocolProvider private constructor() : GenericProtocolProvider, ProtocolTester {
|
||||
companion object {
|
||||
val instance by lazy { SerialProtocolProvider() }
|
||||
const val PROTOCOL = "Serial"
|
||||
}
|
||||
|
||||
override fun getProtocol(): String {
|
||||
return PROTOCOL
|
||||
}
|
||||
|
||||
override fun getIcon(width: Int, height: Int): DynamicIcon {
|
||||
return Icons.serial
|
||||
}
|
||||
|
||||
override fun createTerminalTab(dataProvider: DataProvider, windowScope: WindowScope, host: Host): TerminalTab {
|
||||
return SerialTerminalTab(windowScope, host)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.protocol.ProtocolProvider
|
||||
import app.termora.protocol.ProtocolProviderExtension
|
||||
|
||||
internal class SerialProtocolProviderExtension private constructor() : ProtocolProviderExtension {
|
||||
companion object {
|
||||
val instance by lazy { SerialProtocolProviderExtension() }
|
||||
}
|
||||
|
||||
override fun getProtocolProvider(): ProtocolProvider {
|
||||
return SerialProtocolProvider.instance
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.Host
|
||||
import app.termora.Icons
|
||||
import app.termora.PtyHostTerminalTab
|
||||
import app.termora.WindowScope
|
||||
import app.termora.terminal.PtyConnector
|
||||
import org.apache.commons.io.Charsets
|
||||
import java.nio.charset.StandardCharsets
|
||||
import javax.swing.Icon
|
||||
|
||||
class SerialTerminalTab(windowScope: WindowScope, host: Host) :
|
||||
PtyHostTerminalTab(windowScope, host) {
|
||||
override suspend fun openPtyConnector(): PtyConnector {
|
||||
val serialPort = Serials.openPort(host)
|
||||
return SerialPortPtyConnector(
|
||||
serialPort,
|
||||
Charsets.toCharset(host.options.encoding, StandardCharsets.UTF_8)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getIcon(): Icon {
|
||||
return Icons.plugin
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.Host
|
||||
import app.termora.SerialCommFlowControl
|
||||
import app.termora.SerialCommParity
|
||||
import com.fazecast.jSerialComm.SerialPort
|
||||
|
||||
object Serials {
|
||||
fun openPort(host: Host): SerialPort {
|
||||
val serialComm = host.options.serialComm
|
||||
val serialPort = SerialPort.getCommPort(serialComm.port)
|
||||
serialPort.setBaudRate(serialComm.baudRate)
|
||||
serialPort.setNumDataBits(serialComm.dataBits)
|
||||
|
||||
when (serialComm.parity) {
|
||||
SerialCommParity.None -> serialPort.setParity(SerialPort.NO_PARITY)
|
||||
SerialCommParity.Mark -> serialPort.setParity(SerialPort.MARK_PARITY)
|
||||
SerialCommParity.Even -> serialPort.setParity(SerialPort.EVEN_PARITY)
|
||||
SerialCommParity.Odd -> serialPort.setParity(SerialPort.ODD_PARITY)
|
||||
SerialCommParity.Space -> serialPort.setParity(SerialPort.SPACE_PARITY)
|
||||
}
|
||||
|
||||
when (serialComm.stopBits) {
|
||||
"1" -> serialPort.setNumStopBits(SerialPort.ONE_STOP_BIT)
|
||||
"1.5" -> serialPort.setNumStopBits(SerialPort.ONE_POINT_FIVE_STOP_BITS)
|
||||
"2" -> serialPort.setNumStopBits(SerialPort.TWO_STOP_BITS)
|
||||
}
|
||||
|
||||
when (serialComm.flowControl) {
|
||||
SerialCommFlowControl.None -> serialPort.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED)
|
||||
SerialCommFlowControl.RTS_CTS -> serialPort.setFlowControl(SerialPort.FLOW_CONTROL_RTS_ENABLED or SerialPort.FLOW_CONTROL_CTS_ENABLED)
|
||||
SerialCommFlowControl.XON_XOFF -> serialPort.setFlowControl(SerialPort.FLOW_CONTROL_XONXOFF_IN_ENABLED or SerialPort.FLOW_CONTROL_XONXOFF_OUT_ENABLED)
|
||||
}
|
||||
|
||||
if (!serialPort.openPort()) {
|
||||
throw IllegalStateException("Open serial port [${serialComm.port}] failed")
|
||||
}
|
||||
|
||||
return serialPort
|
||||
}
|
||||
}
|
||||
22
plugins/serial/src/main/resources/META-INF/plugin.xml
Normal file
22
plugins/serial/src/main/resources/META-INF/plugin.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<termora-plugin>
|
||||
|
||||
<id>serial</id>
|
||||
|
||||
<name>Serial Comm</name>
|
||||
|
||||
<version>${projectVersion}</version>
|
||||
|
||||
<termora-version since=">=${rootProjectVersion}" until=""/>
|
||||
|
||||
<entry>app.termora.plugins.serial.SerialPlugin</entry>
|
||||
|
||||
<descriptions>
|
||||
<description>Supports access to serial ports</description>
|
||||
<description language="zh_CN">支持访问串口</description>
|
||||
<description language="zh_TW">支援訪問串口</description>
|
||||
</descriptions>
|
||||
|
||||
<vendor url="https://github.com/TermoraDev">TermoraDev</vendor>
|
||||
|
||||
|
||||
</termora-plugin>
|
||||
@@ -0,0 +1 @@
|
||||
<svg t="1747210120200" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1169" width="16" height="16"><path d="M806.11718723 531.44140652l-78.2578125 78.50390571L410.22265625 291.21874973l82.08984402-78.53906223a231.46874973 231.46874973 0 0 1 162.49218723-68.66015625 206.54296902 206.54296902 0 0 1 131.02734402 44.296875L856.98828125 117.125a35.15625027 35.15625027 0 0 1 49.67578125-0.03515652h0.03515652a35.15625027 35.15625027 0 0 1 0 49.74609429l-74.35546875 74.35546875a225.21093777 225.21093777 0 0 1-26.22656304 290.25z m-191.63671875 27.07031223a24.57421875 24.57421875 0 0 1-1.51171821 33.08203098l-57.72656277 57.76171929 63.98437473 63.73828071-83.42578125 83.46093777a230.62499973 230.62499973 0 0 1-161.71874946 68.66015625 204.92578125 204.92578125 0 0 1-136.75781304-49.21874973l-95.83593723 95.80078071a24.18750027 24.18750027 0 0 1-34.24218723-0.07031223 24.890625 24.890625 0 0 1-0.35156277-34.76953098l95.94140598-100.12500027a225.98437473 225.98437473 0 0 1 21.09375-299.28515598L305.98437473 394.0859375l68.66015625 68.66015625L427.13281277 411.10156277a24.39843777 24.39843777 0 0 1 34.34765598 34.59375L409.94140652 497.234375l117.9140625 117.63281277 56.53125-57.5859375a20.28515652 20.28515652 0 0 1 30.09374946 1.23046848z m-112.32421875 199.19531223l44.05078179-44.05078098-240.22265679-240.46875-44.296875 44.57812473a169.91015625 169.91015625 0 0 0-4.67578071 240.18750027l4.640625 4.92187473a151.875 151.875 0 0 0 117.9140625 48.97265652 175.35937473 175.35937473 0 0 0 122.58984321-54.70312473v0.56249946zM933.875 512c0 232.98046875-188.89453125 421.875-421.875 421.875-7.87499973 0-15.71484375-0.2109375-23.48437473-0.6328125l73.12499946-73.16015598a351.77343777 351.77343777 0 0 0 298.44140679-298.40625027l73.12499946-73.16015598c0.45703152 7.76953098 0.66796902 15.609375 0.66796902 23.48437473zM535.48437473 90.7578125L462.35937527 163.953125a351.77343777 351.77343777 0 0 0-298.44140679 298.40625027l-73.12499946 73.16015598A428.625 428.625 0 0 1 90.125 512C90.125 279.01953125 279.01953125 90.125 512 90.125c7.87499973 0 15.71484375 0.2109375 23.48437473 0.6328125z" fill="#6C707E" p-id="1170"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg t="1747210120200" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1169"
|
||||
width="16" height="16">
|
||||
<path d="M806.11718723 531.44140652l-78.2578125 78.50390571L410.22265625 291.21874973l82.08984402-78.53906223a231.46874973 231.46874973 0 0 1 162.49218723-68.66015625 206.54296902 206.54296902 0 0 1 131.02734402 44.296875L856.98828125 117.125a35.15625027 35.15625027 0 0 1 49.67578125-0.03515652h0.03515652a35.15625027 35.15625027 0 0 1 0 49.74609429l-74.35546875 74.35546875a225.21093777 225.21093777 0 0 1-26.22656304 290.25z m-191.63671875 27.07031223a24.57421875 24.57421875 0 0 1-1.51171821 33.08203098l-57.72656277 57.76171929 63.98437473 63.73828071-83.42578125 83.46093777a230.62499973 230.62499973 0 0 1-161.71874946 68.66015625 204.92578125 204.92578125 0 0 1-136.75781304-49.21874973l-95.83593723 95.80078071a24.18750027 24.18750027 0 0 1-34.24218723-0.07031223 24.890625 24.890625 0 0 1-0.35156277-34.76953098l95.94140598-100.12500027a225.98437473 225.98437473 0 0 1 21.09375-299.28515598L305.98437473 394.0859375l68.66015625 68.66015625L427.13281277 411.10156277a24.39843777 24.39843777 0 0 1 34.34765598 34.59375L409.94140652 497.234375l117.9140625 117.63281277 56.53125-57.5859375a20.28515652 20.28515652 0 0 1 30.09374946 1.23046848z m-112.32421875 199.19531223l44.05078179-44.05078098-240.22265679-240.46875-44.296875 44.57812473a169.91015625 169.91015625 0 0 0-4.67578071 240.18750027l4.640625 4.92187473a151.875 151.875 0 0 0 117.9140625 48.97265652 175.35937473 175.35937473 0 0 0 122.58984321-54.70312473v0.56249946zM933.875 512c0 232.98046875-188.89453125 421.875-421.875 421.875-7.87499973 0-15.71484375-0.2109375-23.48437473-0.6328125l73.12499946-73.16015598a351.77343777 351.77343777 0 0 0 298.44140679-298.40625027l73.12499946-73.16015598c0.45703152 7.76953098 0.66796902 15.609375 0.66796902 23.48437473zM535.48437473 90.7578125L462.35937527 163.953125a351.77343777 351.77343777 0 0 0-298.44140679 298.40625027l-73.12499946 73.16015598A428.625 428.625 0 0 1 90.125 512C90.125 279.01953125 279.01953125 90.125 512 90.125c7.87499973 0 15.71484375 0.2109375 23.48437473 0.6328125z"
|
||||
fill="#CED0D6" p-id="1170"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
Reference in New Issue
Block a user