-
Notifications
You must be signed in to change notification settings - Fork 0
Add video proxy #60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Add video proxy #60
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,388 @@ | ||||||||||||||||||||||
| package com.windhoverlabs.yamcs.util; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import java.io.IOException; | ||||||||||||||||||||||
| import java.net.DatagramPacket; | ||||||||||||||||||||||
| import java.net.DatagramSocket; | ||||||||||||||||||||||
| import java.net.InetSocketAddress; | ||||||||||||||||||||||
| import java.net.ServerSocket; | ||||||||||||||||||||||
| import java.net.Socket; | ||||||||||||||||||||||
| import java.net.SocketException; | ||||||||||||||||||||||
| import java.util.ArrayList; | ||||||||||||||||||||||
| import java.util.Collections; | ||||||||||||||||||||||
| import java.util.HashSet; | ||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||
| import java.util.Set; | ||||||||||||||||||||||
| import java.util.concurrent.Executors; | ||||||||||||||||||||||
| import java.util.concurrent.ThreadFactory; | ||||||||||||||||||||||
| import java.util.concurrent.ThreadPoolExecutor; | ||||||||||||||||||||||
| import java.util.concurrent.atomic.AtomicBoolean; | ||||||||||||||||||||||
| import org.yamcs.ConfigurationException; | ||||||||||||||||||||||
| import org.yamcs.Spec; | ||||||||||||||||||||||
| import org.yamcs.StandardTupleDefinitions; | ||||||||||||||||||||||
| import org.yamcs.ValidationException; | ||||||||||||||||||||||
| import org.yamcs.YConfiguration; | ||||||||||||||||||||||
| import org.yamcs.cmdhistory.CommandHistoryPublisher; | ||||||||||||||||||||||
| import org.yamcs.commanding.PreparedCommand; | ||||||||||||||||||||||
| import org.yamcs.logging.Log; | ||||||||||||||||||||||
| import org.yamcs.parameter.ParameterValue; | ||||||||||||||||||||||
| import org.yamcs.parameter.SystemParametersService; | ||||||||||||||||||||||
| import org.yamcs.protobuf.Yamcs.Value.Type; | ||||||||||||||||||||||
| import org.yamcs.tctm.AbstractThreadedTcDataLink; | ||||||||||||||||||||||
| import org.yamcs.tctm.Link.Status; | ||||||||||||||||||||||
| import org.yamcs.xtce.Parameter; | ||||||||||||||||||||||
| import org.yamcs.yarch.TupleDefinition; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // public class UdpToTcpProxy extends AbstractThreadedTcDataLink implements SystemParametersProducer | ||||||||||||||||||||||
| // { | ||||||||||||||||||||||
| public class UdpToTcpProxy extends AbstractThreadedTcDataLink { | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| private Log log; | ||||||||||||||||||||||
| protected YConfiguration config; | ||||||||||||||||||||||
| protected String linkName; | ||||||||||||||||||||||
| protected AtomicBoolean disabled = new AtomicBoolean(false); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| private int udpPort; | ||||||||||||||||||||||
| private int tcpPort; | ||||||||||||||||||||||
| private Parameter udpPortParam; | ||||||||||||||||||||||
| private Parameter tcpPortParam; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| private DatagramSocket udpSocket; | ||||||||||||||||||||||
| private ServerSocket tcpServerSocket; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| private Thread udpListenerThread; | ||||||||||||||||||||||
| private Thread tcpServerThread; | ||||||||||||||||||||||
| private ThreadPoolExecutor clientHandlerExecutor; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| private static TupleDefinition gftdef = StandardTupleDefinitions.PARAMETER.copy(); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| private Set<Socket> tcpClients = Collections.synchronizedSet(new HashSet<>()); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| private CommandHistoryPublisher commandHistoryPublisher; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @Override | ||||||||||||||||||||||
| public void init(String instance, String name, YConfiguration config) | ||||||||||||||||||||||
| throws ConfigurationException { | ||||||||||||||||||||||
| super.init(instance, name, config); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| this.log = new Log(getClass(), instance); | ||||||||||||||||||||||
| this.config = config; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /* Validate the configuration that the user passed us. */ | ||||||||||||||||||||||
| try { | ||||||||||||||||||||||
| config = getSpec().validate(config); | ||||||||||||||||||||||
| } catch (ValidationException e) { | ||||||||||||||||||||||
| log.error("Failed configuration validation.", e); | ||||||||||||||||||||||
| notifyFailed(e); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| this.linkName = name; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Read configuration | ||||||||||||||||||||||
| this.udpPort = config.getInt("udpPort"); | ||||||||||||||||||||||
| this.tcpPort = config.getInt("tcpPort"); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @Override | ||||||||||||||||||||||
| protected void doStart() { | ||||||||||||||||||||||
| log.info("Starting UdpToTcpProxy: " + getName()); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| try { | ||||||||||||||||||||||
| // Initialize UDP socket | ||||||||||||||||||||||
| udpSocket = new DatagramSocket(udpPort); | ||||||||||||||||||||||
| udpListenerThread = new Thread(this::udpListener); | ||||||||||||||||||||||
| udpListenerThread.setName("UdpListenerThread"); | ||||||||||||||||||||||
| udpListenerThread.start(); | ||||||||||||||||||||||
| log.info("UDP listener started on port " + udpPort); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Initialize TCP server | ||||||||||||||||||||||
| tcpServerSocket = new ServerSocket(); | ||||||||||||||||||||||
| tcpServerSocket.bind(new InetSocketAddress(tcpPort)); | ||||||||||||||||||||||
| tcpServerThread = new Thread(this::tcpServer); | ||||||||||||||||||||||
| tcpServerThread.setName("TcpServerThread"); | ||||||||||||||||||||||
| tcpServerThread.start(); | ||||||||||||||||||||||
| log.info("TCP server started on port " + tcpPort); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Executor for handling client connections | ||||||||||||||||||||||
| clientHandlerExecutor = | ||||||||||||||||||||||
| (ThreadPoolExecutor) | ||||||||||||||||||||||
| Executors.newCachedThreadPool( | ||||||||||||||||||||||
| new ThreadFactory() { | ||||||||||||||||||||||
| private int count = 0; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @Override | ||||||||||||||||||||||
| public Thread newThread(Runnable r) { | ||||||||||||||||||||||
| return new Thread(r, "TcpClientHandler-" + count++); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| super.doStart(); | ||||||||||||||||||||||
| } catch (IOException e) { | ||||||||||||||||||||||
| log.error("Failed to start UdpToTcpProxy: " + e.getMessage()); | ||||||||||||||||||||||
| notifyFailed(e); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @Override | ||||||||||||||||||||||
| protected void doStop() { | ||||||||||||||||||||||
| log.info("Stopping UdpToTcpProxy: " + getName()); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| disabled.set(true); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Close UDP socket | ||||||||||||||||||||||
| if (udpSocket != null && !udpSocket.isClosed()) { | ||||||||||||||||||||||
| udpSocket.close(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Close TCP server socket | ||||||||||||||||||||||
| try { | ||||||||||||||||||||||
| if (tcpServerSocket != null && !tcpServerSocket.isClosed()) { | ||||||||||||||||||||||
| tcpServerSocket.close(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } catch (IOException e) { | ||||||||||||||||||||||
| log.warn("Error closing TCP server socket", e); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Close all client sockets | ||||||||||||||||||||||
| synchronized (tcpClients) { | ||||||||||||||||||||||
| for (Socket client : tcpClients) { | ||||||||||||||||||||||
| try { | ||||||||||||||||||||||
| client.close(); | ||||||||||||||||||||||
| } catch (IOException e) { | ||||||||||||||||||||||
| log.warn("Error closing client socket", e); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| tcpClients.clear(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Shutdown executor | ||||||||||||||||||||||
| if (clientHandlerExecutor != null && !clientHandlerExecutor.isShutdown()) { | ||||||||||||||||||||||
| clientHandlerExecutor.shutdownNow(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| super.doStop(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| private void udpListener() { | ||||||||||||||||||||||
| byte[] buffer = new byte[65535]; | ||||||||||||||||||||||
| DatagramPacket packet = new DatagramPacket(buffer, buffer.length); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| while (!disabled.get() && !udpSocket.isClosed()) { | ||||||||||||||||||||||
| try { | ||||||||||||||||||||||
| udpSocket.receive(packet); | ||||||||||||||||||||||
| int length = packet.getLength(); | ||||||||||||||||||||||
| byte[] data = new byte[length]; | ||||||||||||||||||||||
| System.arraycopy(packet.getData(), packet.getOffset(), data, 0, length); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /* Update PV telemetry */ | ||||||||||||||||||||||
| TupleDefinition tdef = gftdef.copy(); | ||||||||||||||||||||||
| // pushTuple(tdef, cols); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
||||||||||||||||||||||
Copilot
AI
Aug 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The read() call blocks indefinitely and could prevent proper cleanup. Consider using a timeout or non-blocking approach to detect client disconnections more efficiently.
| break; | |
| clientSocket.setSoTimeout(1000); // Set read timeout to 1 second | |
| while (!clientSocket.isClosed()) { | |
| try { | |
| if (clientSocket.getInputStream().read() == -1) { | |
| break; | |
| } | |
| } catch (java.net.SocketTimeoutException e) { | |
| // Timeout occurred, check again if socket is closed | |
| continue; |
Copilot
AI
Aug 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing an element from a synchronized collection while iterating over it can cause a ConcurrentModificationException. Consider using an iterator with remove() or collecting clients to remove in a separate list first.
| } | |
| clientsToRemove.add(client); | |
| } | |
| } | |
| tcpClients.removeAll(clientsToRemove); |
Copilot
AI
Aug 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Large blocks of commented-out code (lines 252-310) should be removed to improve readability. If these methods need to be implemented later, consider adding proper TODO comments instead.
Copilot
AI
Aug 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The startUp() method is empty with only a TODO comment. Either implement the required functionality or remove the comment if no implementation is needed.
| // TODO Auto-generated method stub |
Copilot
AI
Aug 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The shutDown() method is empty with only a TODO comment. Either implement the required cleanup functionality or remove the comment if no implementation is needed.
| // TODO Auto-generated method stub | |
| super.shutDown(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove commented-out code. If this interface implementation is planned for the future, consider adding a TODO comment with context instead.