commit 14/01/2026

This commit is contained in:
2026-01-14 12:29:34 +07:00
parent 8e2d529a9e
commit 934e69c646
6 changed files with 231 additions and 17 deletions

View File

@@ -185,6 +185,14 @@ class Somecodes {
return Path.of(path).fileName.toString()
}
fun ToByteArray(vararg values: Int) : ByteArray {
val byteArray = ByteArray(values.size)
for (i in values.indices){
byteArray[i] = values[i].toByte()
}
return byteArray
}
fun ExtractFilesFromClassPath(resourcePath: String, outputDir: Path) {
try {
val resource = Somecodes::class.java.getResource(resourcePath)

View File

@@ -0,0 +1,24 @@
package toa
@Suppress("unused")
data class SX2KBroadcastFromAI(val AIAddress: Int, val AIChannel: Int, val Zone: ByteArray = ByteArray(32)){
init {
require(AIAddress in 1..8 ) { "AIAddress must be in the range 1-8" }
require(AIChannel in 1..8) { "AIChannel must be in the range 8-8" }
}
fun ZoneSelect(AO: Int, ZoneNumber: Int, Broadcast: Boolean){
if (AO in 1..32 && ZoneNumber in 1..8){
val index = (AO - 1)
val bit = ZoneNumber - 1
if (Broadcast){
Zone[index] = (Zone[index].toInt() or (1 shl bit)).toByte()
} else {
Zone[index] = (Zone[index].toInt() and (1 shl bit).inv()).toByte()
}
}
}
override fun toString(): String {
return "SX2KBroadcastFromAI(AISlot=$AIAddress, AIChannel=$AIChannel)"
}
}

28
src/toa/SX2KCIN.kt Normal file
View File

@@ -0,0 +1,28 @@
package toa
data class SX2KCIN(val device: SX2KDevice, val address: Int, val Cin: Int, val state: Boolean) {
init{
when(device){
SX2KDevice.SX2000SM -> {
require(Cin in 1..8) { "Cin must be between 1 and 8 for SX2000SM" }
}
SX2KDevice.SX2000AO -> {
require(address in 1..32) { "Address must be between 1 and 32 for SX2000AO" }
require(Cin in 1..8) { "Cin must be between 1 and 8 for SX2000AO" }
}
SX2KDevice.SX2000AI -> {
require(address in 1..8) { "Address must be between 1 and 8 for SX2000AI" }
require(Cin in 1..16) { "Cin must be between 1 and 16 for SX2000AI" }
}
else -> throw IllegalArgumentException("Invalid device type")
}
}
override fun toString(): String {
return when(device){
SX2KDevice.SX2000SM -> "SX2000SM - Cin: $Cin, State: $state"
SX2KDevice.SX2000AO -> "SX2000AO - Address: $address, Cin: $Cin, State: $state"
SX2KDevice.SX2000AI -> "SX2000AI - Address: $address, Cin: $Cin, State: $state"
else -> "Unknown Device"
}
}
}

10
src/toa/SX2KDevice.kt Normal file
View File

@@ -0,0 +1,10 @@
package toa
@Suppress("unused")
enum class SX2KDevice(description: String) {
SX2000SM("SX2000SM"),
SX2000AI("SX2000AI"),
SX2000AO("SX2000AO"),
SX2000CI("SX2000CI"),
SX2000CO("SX2000CO")
}

159
src/toa/Sx2K.kt Normal file
View File

@@ -0,0 +1,159 @@
package toa
import codes.Somecodes.Companion.ToByteArray
import org.tinylog.Logger
import java.net.InetSocketAddress
import java.net.Socket
/**
* Created by a Sx2K device.
* @param ipAddress The IP address of the Sx2K device. Default is 192.168.14.1
*/
@Suppress("unused")
class Sx2K(val ipAddress: String = "192.168.14.1") {
private var remotesocket : InetSocketAddress? = null
private val timeout = 2000
init {
try{
val inet = InetSocketAddress(ipAddress, 2005)
// connect trial
val socket = Socket()
socket.soTimeout = 2000
socket.connect(inet, 2000)
socket.close()
remotesocket = inet
} catch (e: Exception) {
Logger.error { "Invalid IP address: $ipAddress , exception : ${e.message}" }
}
}
/**
* Start a BroadcastFromAI on the Sx2K device.
* @param Broadcast Vararg of SX2KBroadcastFromAI objects to start the broadcast
*/
fun StartBroadcastFromAI(vararg Broadcast: SX2KBroadcastFromAI){
if (remotesocket == null) {
Logger.error { "Cannot create BroadcastFromAI, invalid Sx2K device IP address: $ipAddress" }
return
}
try{
Socket().use { socket ->
socket.connect(remotesocket, timeout)
socket.soTimeout = timeout
val outputStream = socket.getOutputStream()
val inputStream = socket.getInputStream()
Broadcast.forEach { bc ->
val cmd : ByteArray = ToByteArray(0xA0, 0xAD, bc.AIAddress, bc.AIChannel) + bc.Zone
outputStream.write(cmd)
outputStream.flush()
val reply = ByteArray(2)
inputStream.read(reply)
if (reply[0]==0.toByte() && reply[1]==1.toByte()){
// DONE TRUE
Logger.info { "BroadcastFromAI succeeded for $bc" }
} else {
Logger.error { "BroadcastFromAI failed for $bc" }
}
}
}
} catch (e: Exception) {
Logger.error { "BroadcastFromAI failed , exception : ${e.message}" }
}
}
/**
* Stop a BroadcastFromAI on the Sx2K device.
* @param Broadcast Vararg of SX2KBroadcastFromAI objects to stop the broadcast
*/
fun StopBroadcastFromAI(vararg Broadcast: SX2KBroadcastFromAI){
if (remotesocket == null) {
Logger.error { "Cannot create StopBroadcastFromAI, invalid Sx2K device IP address: $ipAddress" }
return
}
try{
Socket().use { socket ->
socket.connect(remotesocket, timeout)
socket.soTimeout = timeout
val outputStream = socket.getOutputStream()
val inputStream = socket.getInputStream()
Broadcast.forEach { bc ->
val cmd = ToByteArray(0xA1, 0xAD, bc.AIAddress, bc.AIChannel)
outputStream.write(cmd)
outputStream.flush()
val reply = ByteArray(2)
inputStream.read(reply)
if (reply[0]==0.toByte() && reply[1]==1.toByte()){
// DONE TRUE
Logger.info { "StopBroadcastFromAI succeeded for $bc" }
} else {
Logger.error { "StopBroadcastFromAI failed for $bc" }
}
}
}
} catch (e: Exception) {
Logger.error { "StopBroadcastFromAI failed , exception : ${e.message}" }
}
}
/**
* Set the state of virtual contact inputs on the Sx2K device.
* @param cin Vararg of SX2KCIN objects to set the state
*/
fun VirtualContactInput(vararg cin : SX2KCIN){
if (remotesocket == null) {
Logger.error { "Cannot create VirtualContactInput, invalid Sx2K device IP address: $ipAddress" }
return
}
try {
Socket().use { socket ->
socket.connect(remotesocket, timeout)
socket.soTimeout = timeout
val outputStream = socket.getOutputStream()
val inputStream = socket.getInputStream()
cin.forEach { cc ->
val cmd : ByteArray = when(cc.device){
SX2KDevice.SX2000SM -> {
if (cc.Cin in 1..8){
ToByteArray(0xA0, 0xA0, cc.Cin, if (cc.state) 0x01 else 0x00)
} else ByteArray(0)
}
SX2KDevice.SX2000AO -> {
if (cc.address in 1..32){
if (cc.Cin in 1..8){
ToByteArray(0xA0, 0xA1, cc.address, cc.Cin, if (cc.state) 0x01 else 0x00)
} else ByteArray(0)
} else ByteArray(0)
}
SX2KDevice.SX2000AI -> {
if (cc.address in 1..8){
if (cc.Cin in 1..16){
ToByteArray(0xA0, 0xA2, cc.address, cc.Cin, if (cc.state) 0x01 else 0x00)
} else ByteArray(0)
} else ByteArray(0)
}
else -> ByteArray(0)
}
if (cmd.isNotEmpty()){
outputStream.write(cmd)
outputStream.flush()
val reply = ByteArray(2)
inputStream.read(reply)
if (reply[0]==0.toByte() && reply[1]==1.toByte()){
// DONE TRUE
Logger.info { "VirtualContactInput succeeded for $cc" }
} else {
Logger.error { "VirtualContactInput failed for $cc" }
}
}
}
}
} catch (e : Exception){
Logger.error { "VirtualContactInput failed , exception : ${e.message}" }
}
}
}

View File

@@ -4,7 +4,6 @@ import codes.Somecodes
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.tinylog.Logger
import java.net.Inet4Address
import java.net.InetSocketAddress
import java.net.Socket
@@ -17,6 +16,7 @@ import java.util.function.BiConsumer
* @param ipaddress IP address of the VX3K device, default to 192.168.14.1
* @param port Port number of the VX3K device, from 50050-50053 default to 50053
*/
@Suppress("unused")
class Vx3K(val ipaddress : String = "192.168.14.1", val port : Int = 50053) {
private val remotesocket : InetSocketAddress
init{
@@ -31,21 +31,6 @@ class Vx3K(val ipaddress : String = "192.168.14.1", val port : Int = 50053) {
}
}
/**
* Connect to the VX3K device
* @param timeout Connection timeout in milliseconds, default to 30000 ms
*/
fun Connect(timeout: Int = 30000){
try{
val socket = Socket()
// read timeout 5 seconds
socket.soTimeout = 5000
socket.connect(remotesocket, timeout)
} catch (e : Exception){
Logger.error { "Failed to connect with ${remotesocket.hostName}:${remotesocket.port}, Message: ${e.message}" }
}
}
/**
* Virtual Contact Input (Commmand 0x1001)
* @param ID : Device ID for VX3K, range 0 - 31
@@ -226,7 +211,7 @@ class Vx3K(val ipaddress : String = "192.168.14.1", val port : Int = 50053) {
try{
val tcp = Socket()
tcp.soTimeout = 5000
tcp.connect(remotesocket, 30000)
tcp.connect(remotesocket, 5000)
val outstream = tcp.getOutputStream()
val instream = tcp.getInputStream()
outstream.write(command)