人生や怠のためのリモートコントロール-進歩のエンジン

画像

注意を引くための写真、実際の生活との類似性は遠い



もう1つ記事を書きます。 既に言及したフォルダー「Projects / 4Fun」のプロジェクトの1つについて。 このプロジェクトは4Funとして始まり、4Useとして終わりました。 つまり、今日まで定期的に使用されています。 そして、それはそのようなものでした...







問題1



私たちは皆、テリーを見るのが大好きです。 まあ、ほとんどの人が愛しています。 私も例外ではありません。 しかし、テリーを見るには、それが必要です。 しかし、これにはいくつかの問題がありました。 持っていませんでした(テリー)。 通常、テリーは私が住んでいた賃貸アパートに付いていたので、彼はそこにいませんでした。 しかし、その後、テレビのないアパートに出会いました。 そして、この問題はどういうわけか解決しなければなりませんでした。



決定事項1



私の人生で喉が渇いたので、テリーではなくテレビチューナーを買うことにしました。これはおそらく、同様の状況でコンピューター技術者の頭に浮かぶ最初の考えです。 考え-完了。 私の同僚の1人は、TVチューナーの販売を望んでいました。 以下がその例です。

画像



一般的に、私はそれを買いました。 はい、それだけでした...



第二の問題



リモートコントロールが回復不能に失われ、復元できないことが判明しました。 そして、私はチャンネルを切り替え、ソファに横たわる音(普通で理解できる喜び)を調整したいと思います。 次に、接続されたソリューションを見つける責任を負う脳部門。 すぐに明らかなのは明らかでした-別のリモコンを見つけて、どういうわけかチューナーと友達になります。 しかし、これは適合しませんでした、なぜなら ネイティブのリモートコントロールと一緒に、信号受信機が失われました(かなり前のようです)。 まあ、どういうわけかそれはプログラマーや何かではありません-「私たちには独自の方法があります」(c)。 したがって、それは懇願します...



二次決定



テレビを見るには、ネイティブチューナーアプリであるBeholdTVを使用します。 上下キーを使用してチャンネルを切り替えたり、音を左右に調整したりできます。 そのため、次のことが考えられました。キーストロークをエミュレートするサーバーをサーバーに書き込み、モバイルのクライアントが必要なキーのコードをサーバーに送信すると、すべてが正常になります。 そのため、最終的に判明しました(良い)。



サーバーは、WindowsのC ++およびWinAPIで作成されました。 簡単です。「私はテレビを制御するサーバーです」という形式のUDPメッセージを介してブロードキャストのストリームを開始し、クライアントが接続するのを待ちます。 したがって、どのクライアントもサーバーの場所を見つけることができ、ハードコードIPは必要ありません。 そして、それを正しくします(私は思う)。

クライアントが接続すると、サーバーは着信コマンドのリスニングを開始します。 何かを聞くとすぐに、キーストロークをエミュレートします。 すべてがシンプルで、1つのファイルに収まります。

サーバーコード
// Roco.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <winsock2.h> #pragma comment(lib, "Ws2_32.lib") void broadcastThreadFunction(void *context) { const SOCKET *broadcastSocket = (SOCKET*)context; sockaddr_in broadcastSocketServiceInfo; ZeroMemory(&broadcastSocketServiceInfo, sizeof(broadcastSocketServiceInfo)); broadcastSocketServiceInfo.sin_family = AF_INET; broadcastSocketServiceInfo.sin_addr.s_addr = htonl(INADDR_BROADCAST); broadcastSocketServiceInfo.sin_port = htons(28777); static const char broadcastMessage[] = "ROCO-BROADCAST-MESSAGE"; do { const int result = sendto(*broadcastSocket, broadcastMessage, sizeof(broadcastMessage), 0, (SOCKADDR*)&broadcastSocketServiceInfo, sizeof(broadcastSocketServiceInfo)); if (result == SOCKET_ERROR && ::WSAGetLastError() == WSAENOTSOCK) { break; } ::Sleep(300); } while (true); _endthread(); } int _tmain(int argc, _TCHAR* argv[]) { if (argc >= 2 && _tcscmp(argv[1], _T("/silent")) == 0) { ::ShowWindow(::GetConsoleWindow(), SW_HIDE); } WSADATA wsaData; ZeroMemory(&wsaData, sizeof(wsaData)); printf("Initializing network... "); int result = ::WSAStartup(MAKEWORD(2,2), &wsaData); if (result == NO_ERROR) { printf("Done.\n"); printf("Creating broadcast socket... "); const SOCKET broadcastSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (broadcastSocket != INVALID_SOCKET) { printf("Done.\n"); static const BOOL onValue = TRUE; setsockopt(broadcastSocket, SOL_SOCKET, SO_BROADCAST, (const char*)&onValue, sizeof(onValue)); printf("Starting broadcast thread... "); HANDLE broadcastThreadHandle =(HANDLE)_beginthread(broadcastThreadFunction, 0, (void*)&broadcastSocket); if (broadcastThreadHandle != INVALID_HANDLE_VALUE) { printf("Done.\n"); printf("Creating listen socket... "); const SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (listenSocket != INVALID_SOCKET) { printf("Done.\n"); printf("Binding listen socket... "); sockaddr_in listenSocketServiceInfo; ZeroMemory(&listenSocketServiceInfo, sizeof(listenSocketServiceInfo)); listenSocketServiceInfo.sin_family = AF_INET; listenSocketServiceInfo.sin_addr.s_addr = htonl(INADDR_ANY); listenSocketServiceInfo.sin_port = htons(28666); result = bind(listenSocket, (SOCKADDR*)&listenSocketServiceInfo, sizeof(listenSocketServiceInfo)); if (result != SOCKET_ERROR) { printf("Done.\n"); printf("Listening for incoming connection... "); result = listen(listenSocket, SOMAXCONN); if (result != SOCKET_ERROR) { printf("Done.\n"); unsigned connectionIndex = 0; do { printf("Accepting incoming connection #%d... ", connectionIndex + 1); ::ResumeThread(broadcastThreadHandle); SOCKET commandSocket = accept(listenSocket, NULL, NULL); if (commandSocket != INVALID_SOCKET) { printf("Done.\n"); ::SuspendThread(broadcastThreadHandle); printf("Sending PING to command socket... "); static const char ping[] = "PING"; result = send(commandSocket, ping, sizeof(ping), 0); if (result != SOCKET_ERROR && result == sizeof(ping)) { printf("Done.\n"); printf("Receiving PONG from command socket... "); static char pong[sizeof("PONG")]; pong[0] = '\0'; result = recv(commandSocket, pong, sizeof(pong), 0); if (result != SOCKET_ERROR && result == sizeof(pong) && strcmp(pong, "PONG") == 0) { printf("Done.\n"); unsigned commandIndex = 0; do { printf("Waiting for command #%d...\n", commandIndex + 1); static char command[2]; ZeroMemory(command, sizeof(command)); result = recv(commandSocket, command, sizeof(command), 0); if (result != SOCKET_ERROR && result == sizeof(command)) { enum { CC_KEY_DOWM = 1, CC_KEY_UP = 0 }; const char commandCode = command[0]; const char keyCode = command[1]; static const char res = 1; switch (commandCode) { case CC_KEY_DOWM: { printf("KEY_DOWN(%d)\n", keyCode); keybd_event(keyCode, 0, 0, 0); send(commandSocket, &res, sizeof(res), 0); } break; case CC_KEY_UP: { printf("KEY_UP(%d)\n", keyCode); keybd_event(keyCode, 0, KEYEVENTF_KEYUP, 0); send(commandSocket, &res, sizeof(res), 0); } break; default: { printf("Invalid command received - %d!\n", commandCode); } break; } } else { printf("Could not receive command from socket (error - %d)!\n", ::WSAGetLastError()); break; } ++commandIndex; } while (true); } else { printf("\nCould not receive PONG from command socket (error - %d)!\n", ::WSAGetLastError()); } } else { printf("\nCould not sent PING to command socket (error - %d)!\n", ::WSAGetLastError()); } } else { printf("\nCould not accept incoming connection (error - %d)!\n", ::WSAGetLastError()); } ++connectionIndex; } while (true); } else { printf("\nCould not listen for incoming connection (error - %d)!\n", ::WSAGetLastError()); } } else { printf("\nCould not bind listen socket (error - %d)!\n", ::WSAGetLastError()); } closesocket(listenSocket); } else { printf("\nCould not create listen socket (error - %d)!\n", ::WSAGetLastError()); } } else { printf("\nCould not start broadcast thread!\n"); } ::ResumeThread(broadcastThreadHandle); closesocket(broadcastSocket); ::WaitForSingleObject(broadcastThreadHandle, INFINITE); } else { printf("\nCould not create broadcast socket (error - %d)!\n", ::WSAGetLastError()); } ::WSACleanup(); } else { printf("\nWSAStartup failed (error - %d)!", result); } return 0; }
      
      









サーバーは著名人とともに起動します。 サーバーはコンソールユーティリティであるため(ログを表示する場合は便利です)、起動直後に次の行が必要です。

 ::ShowWindow(::GetConsoleWindow(), SW_HIDE);
      
      







私はAndroidにモバイルを持っているので、クライアントはJavaでネイティブを作成しました。 その結果、このようなスーパーメガインターフェースが得られます。





クライアントソースも非常に簡単です。 プログラムでインターフェイスを生成し、各ボタンでサーバーへのキーコードの送信を停止します。 クライアントを起動するとき、サーバーの場所を探して接続します。 次のようになります。

顧客コード
 package com.dummy.roco; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.Socket; import java.util.Timer; import java.util.TimerTask; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.MulticastLock; import android.os.Bundle; import android.os.StrictMode; import android.os.Vibrator; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.widget.Button; import android.widget.LinearLayout; public class RemoteControlActivity extends Activity { protected static class ButtonInfo { public final String text_; public final int code_; public ButtonInfo(final String text, final int code) { text_ = text; if (code != 0) { code_ = code; } else { code_ = text.codePointAt(0); } } } protected static class CommandButton extends Button { protected ButtonInfo buttonInfo_; protected Socket commandSocket_; protected Vibrator vibrator_; protected Timer commandTimer_; protected final int COMMAND_DELAY = 200; public CommandButton(final Context context, final ButtonInfo buttonInfo, final Socket commandSocket, final Vibrator vibrator) { super(context); buttonInfo_ = buttonInfo; commandSocket_ = commandSocket; vibrator_ = vibrator; setText(buttonInfo_.text_); setTextSize(getTextSize()); setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startCommandTimer(); break; case MotionEvent.ACTION_UP: stopCommandTimer(); break; } return false; } }); } protected void sendCommand(final int commandCode, final int buttonCode) { final byte command[] = { (byte) commandCode, (byte) buttonCode }; try { commandSocket_.getOutputStream().write(command); } catch (Exception exception) { exception.printStackTrace(); } } public void startCommandTimer() { vibrator_.vibrate(10); sendCommand(CC_KEY_DOWM, buttonInfo_.code_); commandTimer_ = new Timer(); commandTimer_.schedule(new TimerTask() { @Override public void run() { sendCommand(CC_KEY_DOWM, buttonInfo_.code_); } }, COMMAND_DELAY, COMMAND_DELAY); } public void stopCommandTimer() { commandTimer_.cancel(); commandTimer_.purge(); commandTimer_ = null; sendCommand(CC_KEY_UP, buttonInfo_.code_); vibrator_.vibrate(10); } } protected static final ButtonInfo buttonInfos_[][] = { { new ButtonInfo("1", 0), new ButtonInfo("2", 0), new ButtonInfo("3", 0) }, { new ButtonInfo("4", 0), new ButtonInfo("5", 0), new ButtonInfo("6", 0) }, { new ButtonInfo("7", 0), new ButtonInfo("8", 0), new ButtonInfo("9", 0) }, { new ButtonInfo("¾", 8), new ButtonInfo("↑", 38), new ButtonInfo("¤", 77) }, { new ButtonInfo("←", 37), new ButtonInfo("®", 13), new ButtonInfo("→", 39) }, { new ButtonInfo("§", 32), new ButtonInfo("↓", 40), new ButtonInfo("«", 27) } }; protected static final int CC_KEY_DOWM = 1; protected static final int CC_KEY_UP = 0; protected final Socket commandSocket_ = new Socket(); protected Vibrator vibrator_; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); vibrator_ = (Vibrator) getSystemService(VIBRATOR_SERVICE); final LinearLayout mainLayout = new LinearLayout(this); mainLayout.setOrientation(LinearLayout.VERTICAL); for (int i = 0; i < buttonInfos_.length; ++i) { final LinearLayout rowLayout = new LinearLayout(this); rowLayout.setOrientation(LinearLayout.HORIZONTAL); for (int j = 0; j < buttonInfos_[i].length; ++j) { final CommandButton button = new CommandButton(this, buttonInfos_[i][j], commandSocket_, vibrator_); final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); layoutParams.weight = 1.0f; rowLayout.addView(button, layoutParams); } final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); layoutParams.weight = 1.0f; mainLayout.addView(rowLayout, layoutParams); } setContentView(mainLayout, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); MulticastLock multicastLock = null; DatagramSocket broadcastSocket = null; try { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .permitAll().build()); final WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); if (wifiManager != null) { multicastLock = wifiManager .createMulticastLock("ROCO-MulticastLock"); } if (multicastLock != null) { multicastLock.acquire(); } broadcastSocket = new DatagramSocket(28777); broadcastSocket.setBroadcast(true); broadcastSocket.setSoTimeout(1000); final byte[] datagramPacketData = new byte["ROCO-BROADCAST-MESSAGE\0" .length()]; final DatagramPacket datagramPacket = new DatagramPacket( datagramPacketData, datagramPacketData.length); broadcastSocket.receive(datagramPacket); if (new String(datagramPacketData) .compareTo("ROCO-BROADCAST-MESSAGE\0") != 0) { throw new Exception("Could not get ROCO server address!"); } commandSocket_.setSoTimeout(500); commandSocket_.connect(new InetSocketAddress(datagramPacket .getAddress().getHostAddress(), 28666), commandSocket_ .getSoTimeout()); final byte ping[] = new byte["PING\0".length()]; commandSocket_.getInputStream().read(ping); if (new String(ping).compareTo("PING\0") != 0) { throw new Exception( "Could not receive PING from command socket!"); } commandSocket_.getOutputStream().write( new String("PONG\0").getBytes()); } catch (Exception exception) { final AlertDialog alertDialog = new AlertDialog.Builder(this) .create(); alertDialog.setCancelable(false); alertDialog.setTitle("Roco: Error"); alertDialog .setMessage("Could not connect to the server!\nError - '" + exception.toString() + "'\n\nExiting..."); alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); finish(); } }); alertDialog.show(); } finally { if (broadcastSocket != null) { broadcastSocket.close(); } if (multicastLock != null && multicastLock.isHeld()) { multicastLock.release(); } } } @Override protected void onDestroy() { try { commandSocket_.close(); } catch (Exception exception) { exception.printStackTrace(); } super.onDestroy(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_BACK)) { finish(); } return super.onKeyDown(keyCode, event); } }
      
      









このプロジェクトはずっと前に書かれたものであり、プロ意識はまだありません。 さて、おそらく、私はすべてをそれほど不器用ではありません(おそらく、プロジェクトの作成後に学んだ公式APIを使用することさえあります)。 それにもかかわらず、すべてが安定して動作し、定期的に使用されます。



面白い副作用があります-テレビではなく、たとえばYouTubeを見ると、プレーヤーを一時停止できます。 また、一時停止します。



一般的に、それはクールで、便利で、安くて陽気なものでした。



このプロジェクトはロコと呼ばれています。 これがなぜそうなのか誰が推測するのか-コメントに書いてください。 栄光と尊敬を推測した。



PSところで、最近、私はめったに見ません。 主にダウンロードした映画またはオンライン。 私がそれを買わなかったのはいいことです。 しかし、今私は購入を考えています。 何らかのパラドックスが判明します...



All Articles