]> git.sesse.net Git - casparcg/commitdiff
Merged CLK changes from trunk, and separated delimiter message splitting and codepage...
authorhellgore <hellgore@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Tue, 25 Sep 2012 15:28:25 +0000 (15:28 +0000)
committerhellgore <hellgore@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Tue, 25 Sep 2012 15:28:25 +0000 (15:28 +0000)
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches/2.1.0@3365 362d55ac-95cf-4e76-9f9a-cbaa9c17b72d

18 files changed:
common/blocking_priority_queue.h
common/semaphore.h
protocol/clk/CLKCommand.cpp [deleted file]
protocol/clk/CLKProtocolStrategy.cpp
protocol/clk/CLKProtocolStrategy.h
protocol/clk/clk_command_processor.cpp [new file with mode: 0644]
protocol/clk/clk_command_processor.h [new file with mode: 0644]
protocol/clk/clk_commands.cpp [new file with mode: 0644]
protocol/clk/clk_commands.h [moved from protocol/clk/CLKCommand.h with 59% similarity]
protocol/protocol.vcxproj
protocol/protocol.vcxproj.filters
protocol/util/AsyncEventServer.cpp
protocol/util/AsyncEventServer.h
protocol/util/ProtocolStrategy.h
protocol/util/protocol_strategy.h [new file with mode: 0644]
protocol/util/strategy_adapters.cpp [new file with mode: 0644]
protocol/util/strategy_adapters.h [new file with mode: 0644]
shell/server.cpp

index 101ae31b0954e29ff78f454ea19cadbdfc885949..f8cd0198cb4441a2cafb9e8439c039197c2a3cd2 100644 (file)
@@ -124,7 +124,6 @@ public:
        {
                acquire_transaction transaction(elements_available_);
 
-
                pop_acquired_any_priority(element, transaction);
        }
 
index 1a384397e99c936ab849ed1bacac3cff5023c015..c50394f852e488feefb1c4629a918314218c988b 100644 (file)
@@ -110,15 +110,18 @@ public:
                boost::mutex::scoped_lock lock(mutex_);
                auto num_acquired = 0u;
 
-               while (permits_ == 0u && num_acquired < permits)
+               while (true)
                {
-                       permits_available_.wait(lock);
-
                        auto num_wanted = permits - num_acquired;
                        auto to_drain = std::min(num_wanted, permits_);
 
                        permits_ -= to_drain;
                        num_acquired += to_drain;
+
+                       if (num_acquired == permits)
+                               break;
+
+                       permits_available_.wait(lock);
                }
        }
 
diff --git a/protocol/clk/CLKCommand.cpp b/protocol/clk/CLKCommand.cpp
deleted file mode 100644 (file)
index 23813d3..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
-* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
-*
-* This file is part of CasparCG (www.casparcg.com).
-*
-* CasparCG is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation, either version 3 of the License, or
-* (at your option) any later version.
-*
-* CasparCG is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
-*
-* Author: Nicklas P Andersson
-*/
-
-#include "..\stdafx.h"
-#include <algorithm>
-#include <locale>
-#include "CLKCommand.h"
-
-namespace caspar { namespace protocol { namespace CLK {
-
-CLKCommand::CLKCommand() : clockID_(0), command_(CLKInvalidCommand) {}
-
-CLKCommand::~CLKCommand() {}
-
-const std::wstring& CLKCommand::GetData() 
-{
-       std::wstringstream dataStream;
-
-       dataStream << TEXT("<templateData>");   
-       dataStream << TEXT("<componentData id=\"command\">");
-       dataStream << TEXT("<command id=\"") << commandString_ << TEXT("\" time=\"") << time_ << TEXT("\" clockID=\"") << clockID_ << TEXT("\">");
-
-       std::vector<std::wstring>::const_iterator it = parameters_.begin();
-       std::vector<std::wstring>::const_iterator end = parameters_.end();
-       for(; it != end; ++it) {
-               dataStream << TEXT("<parameter>") << (*it) << TEXT("</parameter>"); 
-       }
-
-       dataStream << TEXT("</command>"); 
-       dataStream << TEXT("</componentData>"); 
-       dataStream << TEXT("</templateData>");
-
-       dataCache_ = dataStream.str();
-       return dataCache_;
-}
-
-bool CLKCommand::SetCommand() 
-{
-       bool bResult = true;
-       std::transform(commandString_.begin(), commandString_.end(), commandString_.begin(), toupper);
-
-       if(commandString_ == TEXT("DUR"))
-               command_ = CLKDuration;
-       else if(commandString_ == TEXT("NEWDUR"))
-               command_ = CLKNewDuration;
-       else if(commandString_ == TEXT("NEXTEVENT"))
-               command_ = CLKNextEvent;
-       else if(commandString_ == TEXT("STOP"))
-               command_ = CLKStop;
-       else if(commandString_ == TEXT("UNTIL"))
-               command_ = CLKUntil;
-       else if(commandString_ == TEXT("ADD"))
-               command_ = CLKAdd;
-       else if(commandString_ == TEXT("SUB"))
-               command_ = CLKSub;
-       else if(commandString_ == TEXT("RESET"))
-               command_ = CLKReset;
-       else 
-       {
-               command_ = CLKInvalidCommand;
-               bResult = false;
-       }
-
-       return bResult;
-}
-
-void CLKCommand::Clear() 
-{
-       dataCache_.clear();
-       commandString_.clear();
-       time_.clear();
-       command_ = CLKDuration;
-       clockID_ = 0;
-       parameters_.clear();
-}
-
-}}}
\ No newline at end of file
index 531b75932951f5c44fdf5b1fb17ab87460a4986a..30b5f7d9aeb9d4ccff54242ba5a3de200922a025 100644 (file)
 #include "..\stdafx.h"
 
 #include "CLKProtocolStrategy.h"
+#include "clk_commands.h"
 
 #include <modules/flash/producer/cg_proxy.h>
 
 #include <string>
-#include <sstream>
 #include <algorithm>
+#include <locale>
+#include <vector>
+#include <sstream>
 
 namespace caspar { namespace protocol { namespace CLK {
-       
-CLKProtocolStrategy::CLKProtocolStrategy(const std::vector<spl::shared_ptr<core::video_channel>>& channels) 
-       : currentState_(ExpectingNewCommand), bClockLoaded_(false), pChannel_(channels.at(0))
-{}
 
-void CLKProtocolStrategy::Parse(const TCHAR* pData, int charCount, IO::ClientInfoPtr pClientInfo) 
+class CLKProtocolStrategy : public IO::protocol_strategy<wchar_t>
 {
-       for(int index = 0; index < charCount; ++index) 
+public:
+       CLKProtocolStrategy(
+               const IO::client_connection<wchar_t>::ptr& client_connection,
+               clk_command_processor& command_processor) 
+               : currentState_(ExpectingNewCommand)
+               , command_processor_(command_processor)
+               , client_connection_(client_connection)
        {
-               if(currentState_ == ExpectingNewCommand)
-                       currentCommandString_.str(TEXT(""));
-
-               TCHAR currentByte = pData[index];
-               if(currentByte < 32)
-                       currentCommandString_ << TEXT("<") << (int)currentByte << TEXT(">");
-               else
-                       currentCommandString_ << currentByte;
+       }
 
-               if(currentByte != 0)
+       void parse(const std::basic_string<wchar_t>& data)
+       {
+               for (int index = 0; index < data.length(); ++index) 
                {
-                       switch(currentState_)
+                       wchar_t currentByte = data[index];
+
+                       if (currentByte < 32)
+                               currentCommandString_ << L"<" << static_cast<int>(currentByte) << L">";
+                       else
+                               currentCommandString_ << currentByte;
+
+                       if (currentByte != 0)
                        {
-                               case ExpectingNewCommand:
-                                       if(currentByte == 1) 
-                                               currentState_ = ExpectingCommand;                                       
-                                       //just throw anything else away
-                                       break;
-
-                               case ExpectingCommand:
-                                       if(currentByte == 2) 
-                                       {
-                                               if(!currentCommand_.SetCommand()) 
+                               switch (currentState_)
+                               {
+                                       case ExpectingNewCommand:
+                                               if (currentByte == 1) 
+                                                       currentState_ = ExpectingCommand;                                       
+                                               //just throw anything else away
+                                               break;
+                                       case ExpectingCommand:
+                                               if (currentByte == 2) 
+                                                       currentState_ = ExpectingParameter;
+                                               else
+                                                       command_name_ += currentByte;
+                                               break;
+                                       case ExpectingParameter:
+                                               //allocate new parameter
+                                               if (parameters_.size() == 0 || currentByte == 2)
+                                                       parameters_.push_back(std::wstring());
+
+                                               //add the character to end end of the last parameter
+                                               if (currentByte != 2)
                                                {
-                                                       CASPAR_LOG(error) << "CLK: Failed to interpret command";
-                                                       currentState_ = ExpectingNewCommand;
-                                                       currentCommand_.Clear();
+                                                       //add the character to end end of the last parameter
+                                                       if (currentByte == L'<')
+                                                               parameters_.back() += L"&lt;";
+                                                       else if (currentByte == L'>')
+                                                               parameters_.back() += L"&gt;";
+                                                       else if (currentByte == L'\"')
+                                                               parameters_.back() += L"&quot;";
+                                                       else
+                                                               parameters_.back() += currentByte;
                                                }
-                                               else 
-                                                       currentState_ = ExpectingClockID;                                               
-                                       }
-                                       else
-                                               currentCommand_.commandString_ += currentByte;
-                                       break;
 
-                               case ExpectingClockID:
-                                       if(currentByte == 2)
-                                               currentState_ = currentCommand_.NeedsTime() ? ExpectingTime : ExpectingParameter;
-                                       else
-                                               currentCommand_.clockID_ = currentByte - TCHAR('0');
-                                       break;
-
-                               case ExpectingTime:
-                                       if(currentByte == 2)
-                                               currentState_ = ExpectingParameter;
-                                       else
-                                               currentCommand_.time_ += currentByte;
-                                       break;
-
-                               case ExpectingParameter:
-                                       //allocate new parameter
-                                       if(currentCommand_.parameters_.size() == 0 || currentByte == 2)
-                                               currentCommand_.parameters_.push_back(std::wstring());
-
-                                       //add the character to end end of the last parameter
-                                       if(currentByte == TEXT('<'))
-                                               currentCommand_.parameters_[currentCommand_.parameters_.size()-1] += TEXT("&lt;");
-                                       else if(currentByte == TEXT('>'))
-                                               currentCommand_.parameters_[currentCommand_.parameters_.size()-1] += TEXT("&gt;");
-                                       else if(currentByte == TEXT('\"'))
-                                               currentCommand_.parameters_[currentCommand_.parameters_.size()-1] += TEXT("&quot;");
-                                       else
-                                               currentCommand_.parameters_[currentCommand_.parameters_.size()-1] += currentByte;
-
-                                       break;
+                                               break;
+                               }
                        }
-               }
-               else 
-               {
-                       if(currentState_ == ExpectingCommand)
+                       else
                        {
-                               if(!currentCommand_.SetCommand())
-                                       CASPAR_LOG(error) << "CLK: Failed to interpret command";
-                       }
+                               std::transform(
+                                       command_name_.begin(), command_name_.end(), 
+                                       command_name_.begin(), 
+                                       toupper);
 
-                       if(currentCommand_.command_ == CLKCommand::CLKReset) 
-                       {
-                               pChannel_->stage().clear(flash::cg_proxy::DEFAULT_LAYER);
-                               bClockLoaded_ = false;
-                               
-                               CASPAR_LOG(info) << L"CLK: Recieved and executed reset-command";
-                       }
-                       else if(currentCommand_.command_ != CLKCommand::CLKInvalidCommand)
-                       {
-                               if(!bClockLoaded_) 
+                               try
                                {
-                                       flash::create_cg_proxy(pChannel_).add(0, TEXT("hawrysklocka/clock.ft"), true, TEXT(""), currentCommand_.GetData());
-                                       bClockLoaded_ = true;
+                                       if (!command_processor_.handle(command_name_, parameters_))
+                                               CASPAR_LOG(error) << "CLK: Unknown command: " << command_name_;
+                                       else
+                                               CASPAR_LOG(debug) << L"CLK: Executed valid command: " 
+                                                       << currentCommandString_.str();
+                               } 
+                               catch (...)
+                               {
+                                       CASPAR_LOG_CURRENT_EXCEPTION();
+                                       CASPAR_LOG(error) << "CLK: Failed to interpret command: " 
+                                               << currentCommandString_.str();
                                }
-                               else 
-                                       flash::create_cg_proxy(pChannel_).update(0, currentCommand_.GetData());
-                               
-                               CASPAR_LOG(debug) << L"CLK: Clockdata sent: " << currentCommand_.GetData();
-                               CASPAR_LOG(debug) << L"CLK: Executed valid command: " << currentCommandString_.str();
-                       }
 
-                       currentState_ = ExpectingNewCommand;
-                       currentCommand_.Clear();
+                               reset();
+                       }
                }
        }
+private:
+       void reset()
+       {
+               currentState_ = ExpectingNewCommand;
+               currentCommandString_.str(L"");
+               command_name_.clear();
+               parameters_.clear();
+       }
+
+       enum ParserState
+       {
+               ExpectingNewCommand,
+               ExpectingCommand,
+               ExpectingParameter
+       };
+
+       ParserState     currentState_;
+       std::wstringstream currentCommandString_;
+       std::wstring command_name_;
+       std::vector<std::wstring> parameters_;
+       clk_command_processor& command_processor_;
+       IO::client_connection<wchar_t>::ptr client_connection_;
+};
+
+clk_protocol_strategy_factory::clk_protocol_strategy_factory(
+               const std::vector<spl::shared_ptr<core::video_channel>>& channels)
+{
+       add_command_handlers(command_processor_, channels.at(0));
+}
+
+IO::protocol_strategy<wchar_t>::ptr clk_protocol_strategy_factory::create(
+               const IO::client_connection<wchar_t>::ptr& client_connection)
+{
+       return spl::make_shared<CLKProtocolStrategy>(client_connection, command_processor_);
 }
 
-}      //namespace CLK
-}}     //namespace caspar
\ No newline at end of file
+}}}
index b813b540ec92a04e4790c064d0d2b0d068d3ef6a..c82534cecef645b1cfd50af655e05bde92e8b601 100644 (file)
  
 #pragma once
 
-#include "CLKCommand.h"
-#include "../util/ProtocolStrategy.h"
+#include "../util/protocol_strategy.h"
+#include "clk_command_processor.h"
 #include <core/video_channel.h>
 
-#include <string>
-
 namespace caspar { namespace protocol { namespace CLK {
 
-class CLKProtocolStrategy : public IO::IProtocolStrategy
+class clk_protocol_strategy_factory : public IO::protocol_strategy_factory<wchar_t>
 {
+       clk_command_processor command_processor_;
 public:
-       CLKProtocolStrategy(const std::vector<spl::shared_ptr<core::video_channel>>& channels);
-
-       void Parse(const TCHAR* pData, int charCount, IO::ClientInfoPtr pClientInfo);
-       std::string GetCodepage() { return "ISO-8859-1"; }      //ISO 8859-1
-       
-private:
-       enum ParserState
-       {
-               ExpectingNewCommand,
-               ExpectingCommand,
-               ExpectingClockID,
-               ExpectingTime,
-               ExpectingParameter
-       };
-
-       ParserState     currentState_;
-       CLKCommand currentCommand_;
-       std::wstringstream currentCommandString_;
-
-       spl::shared_ptr<core::video_channel> pChannel_;
+       clk_protocol_strategy_factory(
+                       const std::vector<spl::shared_ptr<core::video_channel>>& channels);
 
-       bool bClockLoaded_;
+       virtual IO::protocol_strategy<wchar_t>::ptr create(
+               const IO::client_connection<wchar_t>::ptr& client_connection);
 };
 
 }}}
diff --git a/protocol/clk/clk_command_processor.cpp b/protocol/clk/clk_command_processor.cpp
new file mode 100644 (file)
index 0000000..d614a6a
--- /dev/null
@@ -0,0 +1,49 @@
+/*\r
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
+*\r
+* This file is part of CasparCG (www.casparcg.com).\r
+*\r
+* CasparCG is free software: you can redistribute it and/or modify\r
+* it under the terms of the GNU General Public License as published by\r
+* the Free Software Foundation, either version 3 of the License, or\r
+* (at your option) any later version.\r
+*\r
+* CasparCG is distributed in the hope that it will be useful,\r
+* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+* GNU General Public License for more details.\r
+*\r
+* You should have received a copy of the GNU General Public License\r
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
+*\r
+* Author: Helge Norberg, helge.norberg@svt.se\r
+*/\r
+\r
+#include "../stdafx.h"\r
+\r
+#include "clk_command_processor.h"\r
+\r
+namespace caspar { namespace protocol { namespace CLK {\r
+\r
+clk_command_processor& clk_command_processor::add_handler(\r
+       const std::wstring& command_name, const clk_command_handler& handler)\r
+{\r
+       handlers_.insert(std::make_pair(command_name, handler));\r
+\r
+       return *this;\r
+}\r
+\r
+bool clk_command_processor::handle(\r
+       const std::wstring& command_name, const std::vector<std::wstring>& parameters)\r
+{\r
+       auto handler = handlers_.find(command_name);\r
+\r
+       if (handler == handlers_.end())\r
+               return false;\r
+\r
+       handler->second(parameters);\r
+\r
+       return true;\r
+}\r
+\r
+}}}\r
diff --git a/protocol/clk/clk_command_processor.h b/protocol/clk/clk_command_processor.h
new file mode 100644 (file)
index 0000000..8313902
--- /dev/null
@@ -0,0 +1,71 @@
+/*\r
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
+*\r
+* This file is part of CasparCG (www.casparcg.com).\r
+*\r
+* CasparCG is free software: you can redistribute it and/or modify\r
+* it under the terms of the GNU General Public License as published by\r
+* the Free Software Foundation, either version 3 of the License, or\r
+* (at your option) any later version.\r
+*\r
+* CasparCG is distributed in the hope that it will be useful,\r
+* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+* GNU General Public License for more details.\r
+*\r
+* You should have received a copy of the GNU General Public License\r
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
+*\r
+* Author: Helge Norberg, helge.norberg@svt.se\r
+*/\r
+\r
+#pragma once\r
+\r
+#include <vector>\r
+#include <string>\r
+#include <map>\r
+\r
+#include <common/memory.h>\r
+\r
+#include <boost/function.hpp>\r
+\r
+namespace caspar { namespace protocol { namespace CLK {\r
+\r
+typedef boost::function<void (const std::vector<std::wstring>&)> clk_command_handler;\r
+\r
+/**\r
+ * CLK command processor composed by multiple command handlers.\r
+ *\r
+ * Not thread-safe.\r
+ */\r
+class clk_command_processor\r
+{\r
+       std::map<std::wstring, clk_command_handler> handlers_;\r
+public:\r
+       /**\r
+        * Register a handler for a specific command.\r
+        *\r
+        * @param command_name The command name to use this handler for.\r
+        * @param handler      The handler that will handle all commands with the\r
+        *                     specified name.\r
+        *\r
+        * @return this command processor for method chaining.\r
+        */\r
+       clk_command_processor& add_handler(\r
+               const std::wstring& command_name, const clk_command_handler& handler);\r
+\r
+       /**\r
+        * Handle an incoming command.\r
+        *\r
+        * @param command_name The command name.\r
+        * @param parameters   The raw parameters supplied with the command.\r
+        *\r
+        * @return true if the command was handled, false if no handler was\r
+        *         registered to handle the command.\r
+        */\r
+       bool handle(\r
+               const std::wstring& command_name, \r
+               const std::vector<std::wstring>& parameters);\r
+};\r
+\r
+}}}\r
diff --git a/protocol/clk/clk_commands.cpp b/protocol/clk/clk_commands.cpp
new file mode 100644 (file)
index 0000000..95b218d
--- /dev/null
@@ -0,0 +1,182 @@
+/*\r
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
+*\r
+* This file is part of CasparCG (www.casparcg.com).\r
+*\r
+* CasparCG is free software: you can redistribute it and/or modify\r
+* it under the terms of the GNU General Public License as published by\r
+* the Free Software Foundation, either version 3 of the License, or\r
+* (at your option) any later version.\r
+*\r
+* CasparCG is distributed in the hope that it will be useful,\r
+* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+* GNU General Public License for more details.\r
+*\r
+* You should have received a copy of the GNU General Public License\r
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
+*\r
+* Author: Helge Norberg, helge.norberg@svt.se\r
+*/\r
+\r
+#include "../stdafx.h"\r
+\r
+#include <stdexcept>\r
+#include <sstream>\r
+\r
+#include <boost/lexical_cast.hpp>\r
+\r
+#include <common/log.h>\r
+\r
+#include <modules/flash/producer/cg_proxy.h>\r
+\r
+#include "clk_commands.h"\r
+\r
+namespace caspar { namespace protocol { namespace CLK {\r
+\r
+class command_context\r
+{\r
+       bool clock_loaded_;\r
+       spl::shared_ptr<core::video_channel> channel_;\r
+public:\r
+       command_context(const spl::shared_ptr<core::video_channel>& channel)\r
+               : clock_loaded_(false)\r
+               , channel_(channel)\r
+       {\r
+       }\r
+\r
+       void send_to_flash(const std::wstring& data)\r
+       {\r
+               if (!clock_loaded_) \r
+               {\r
+                       flash::create_cg_proxy(channel_).add(\r
+                               0, L"hawrysklocka/clock.ft", true, L"", data);\r
+                       clock_loaded_ = true;\r
+               }\r
+               else\r
+               {\r
+                       flash::create_cg_proxy(channel_).update(0, data);\r
+               }\r
+                               \r
+               CASPAR_LOG(debug) << L"CLK: Clockdata sent: " << data;\r
+       }\r
+\r
+       void reset()\r
+       {\r
+               channel_->stage().clear(flash::cg_proxy::DEFAULT_LAYER);\r
+               clock_loaded_ = false;\r
+               CASPAR_LOG(info) << L"CLK: Recieved and executed reset-command";\r
+       }\r
+};\r
+\r
+template<class T>\r
+T require_param(\r
+       std::vector<std::wstring>::const_iterator& params_current,\r
+       const std::vector<std::wstring>::const_iterator& params_end,\r
+       const std::string& param_name)\r
+{\r
+       if (params_current == params_end)\r
+               throw std::runtime_error(param_name + " required");\r
+\r
+       T value = boost::lexical_cast<T>(*params_current);\r
+\r
+       ++params_current;\r
+\r
+       return std::move(value);\r
+}\r
+\r
+std::wstring get_xml(\r
+       const std::wstring& command_name,\r
+       bool has_clock_id,\r
+       bool has_time,\r
+       const std::vector<std::wstring>& parameters)\r
+{\r
+       std::wstringstream stream;\r
+\r
+       stream << L"<templateData>";    \r
+       stream << L"<componentData id=\"command\">";\r
+       stream << L"<command id=\"" << command_name << "\"";\r
+       \r
+       std::vector<std::wstring>::const_iterator it = parameters.begin();\r
+       std::vector<std::wstring>::const_iterator end = parameters.end();\r
+\r
+       if (has_clock_id)\r
+       {\r
+               stream << L" clockID=\""\r
+                       << require_param<int>(it, end, "clock id") << L"\"";\r
+       }\r
+\r
+       if (has_time)\r
+       {\r
+               stream << L" time=\""\r
+                       << require_param<std::wstring>(it, end, "time") << L"\"";\r
+       }\r
+\r
+       bool has_parameters = it != end;\r
+\r
+       stream << (has_parameters ? L">" : L" />");\r
+\r
+       if (has_parameters)\r
+       {\r
+               for (; it != end; ++it)\r
+               {\r
+                       stream << L"<parameter>" << (*it) << L"</parameter>";\r
+               }\r
+\r
+               stream << L"</command>";\r
+       }\r
+\r
+       stream << L"</componentData>";\r
+       stream << L"</templateData>";\r
+\r
+       return stream.str();\r
+}\r
+\r
+clk_command_handler create_send_xml_handler(\r
+       const std::wstring& command_name, \r
+       bool expect_clock, \r
+       bool expect_time, \r
+       const spl::shared_ptr<command_context>& context)\r
+{\r
+       return [=] (const std::vector<std::wstring>& params)\r
+       {\r
+               context->send_to_flash(get_xml(\r
+                       command_name, expect_clock, expect_time, params));\r
+       };\r
+}\r
+\r
+void add_command_handlers(\r
+       clk_command_processor& processor, \r
+       const spl::shared_ptr<core::video_channel>& channel)\r
+{\r
+       auto context = spl::make_shared<command_context>(channel);\r
+\r
+       processor\r
+               .add_handler(L"DUR", \r
+                       create_send_xml_handler(L"DUR", true, true, context))\r
+               .add_handler(L"NEWDUR", \r
+                       create_send_xml_handler(L"NEWDUR", true, true, context))\r
+               .add_handler(L"UNTIL", \r
+                       create_send_xml_handler(L"UNTIL", true, true, context))\r
+               .add_handler(L"NEXTEVENT", \r
+                       create_send_xml_handler(L"NEXTEVENT", true, false, context))\r
+               .add_handler(L"STOP", \r
+                       create_send_xml_handler(L"STOP", true, false, context))\r
+               .add_handler(L"ADD", \r
+                       create_send_xml_handler(L"ADD", true, true, context))\r
+               .add_handler(L"SUB", \r
+                       create_send_xml_handler(L"SUB", true, true, context))\r
+               .add_handler(L"TIMELINE_LOAD", \r
+                       create_send_xml_handler(L"TIMELINE_LOAD", false, false, context))\r
+               .add_handler(L"TIMELINE_PLAY", \r
+                       create_send_xml_handler(L"TIMELINE_PLAY", false, false, context))\r
+               .add_handler(L"TIMELINE_STOP", \r
+                       create_send_xml_handler(L"TIMELINE_STOP", false, false, context))\r
+               .add_handler(L"RESET", [=] (const std::vector<std::wstring>& params)\r
+               {\r
+                       context->reset();\r
+               })\r
+               ;\r
+}\r
+\r
+}}}\r
similarity index 59%
rename from protocol/clk/CLKCommand.h
rename to protocol/clk/clk_commands.h
index 72e6c92d6bfcf7cbc94bc83a11977ba2eeeff51d..02baf0e147a3e383c8872821ffb02ce4a2571f66 100644 (file)
@@ -1,63 +1,40 @@
-/*
-* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
-*
-* This file is part of CasparCG (www.casparcg.com).
-*
-* CasparCG is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation, either version 3 of the License, or
-* (at your option) any later version.
-*
-* CasparCG is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
-*
-* Author: Nicklas P Andersson
-*/
-
-#pragma once
-
-namespace caspar { namespace protocol { namespace CLK {
-
-class CLKCommand
-{
-public:
-       enum CLKCommands 
-       {
-               CLKDuration,
-               CLKNewDuration,
-               CLKNextEvent,
-               CLKStop,
-               CLKUntil,
-               CLKAdd,
-               CLKSub,
-               CLKReset,
-               CLKInvalidCommand
-       };
-
-       CLKCommand();
-       virtual ~CLKCommand();
-
-       bool SetCommand();
-       bool NeedsTime() const 
-       {
-               return !(command_ == CLKNextEvent || command_ == CLKStop);
-       }
-
-       void Clear();
-       const std::wstring& GetData();
-
-       std::wstring dataCache_;
-       std::wstring commandString_;
-       CLKCommands command_;
-       int clockID_;
-       std::wstring time_;
-       std::vector<std::wstring> parameters_;
-};
-
-}}}
\ No newline at end of file
+/*\r
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
+*\r
+* This file is part of CasparCG (www.casparcg.com).\r
+*\r
+* CasparCG is free software: you can redistribute it and/or modify\r
+* it under the terms of the GNU General Public License as published by\r
+* the Free Software Foundation, either version 3 of the License, or\r
+* (at your option) any later version.\r
+*\r
+* CasparCG is distributed in the hope that it will be useful,\r
+* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+* GNU General Public License for more details.\r
+*\r
+* You should have received a copy of the GNU General Public License\r
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
+*\r
+* Author: Helge Norberg, helge.norberg@svt.se\r
+*/\r
+\r
+#pragma once\r
+\r
+#include <core/video_channel.h>\r
+\r
+#include "clk_command_processor.h"\r
+\r
+namespace caspar { namespace protocol { namespace CLK {\r
+\r
+/**\r
+ * Add the CLK command handlers to a command processor.\r
+ *\r
+ * @param processor The command processor to add the command handlers to.\r
+ * @param channel   The channel to play the flash graphics on.\r
+ */\r
+void add_command_handlers(\r
+       clk_command_processor& processor, \r
+       const spl::shared_ptr<core::video_channel>& channel);\r
+\r
+}}}\r
index 27f37eeea7ec7cbfd289f7e81963e47599d200a7..b48b49c7502d83bc75b1c07580a11653de35fdc3 100644 (file)
@@ -35,8 +35,9 @@
     <ClInclude Include="cii\CIICommand.h" />\r
     <ClInclude Include="cii\CIICommandsImpl.h" />\r
     <ClInclude Include="cii\CIIProtocolStrategy.h" />\r
-    <ClInclude Include="clk\CLKCommand.h" />\r
     <ClInclude Include="clk\CLKProtocolStrategy.h" />\r
+    <ClInclude Include="clk\clk_commands.h" />\r
+    <ClInclude Include="clk\clk_command_processor.h" />\r
     <ClInclude Include="osc\oscpack\MessageMappingOscPacketListener.h" />\r
     <ClInclude Include="osc\oscpack\OscException.h" />\r
     <ClInclude Include="osc\oscpack\OscHostEndianness.h" />\r
@@ -50,6 +51,8 @@
     <ClInclude Include="util\AsyncEventServer.h" />\r
     <ClInclude Include="util\ClientInfo.h" />\r
     <ClInclude Include="util\ProtocolStrategy.h" />\r
+    <ClInclude Include="util\protocol_strategy.h" />\r
+    <ClInclude Include="util\strategy_adapters.h" />\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClCompile Include="amcp\AMCPCommandQueue.cpp">\r
       <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
       <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
     </ClCompile>\r
-    <ClCompile Include="clk\CLKCommand.cpp">\r
+    <ClCompile Include="clk\CLKProtocolStrategy.cpp">\r
       <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
       <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
     </ClCompile>\r
-    <ClCompile Include="clk\CLKProtocolStrategy.cpp">\r
+    <ClCompile Include="clk\clk_commands.cpp">\r
+      <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
       <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
+    </ClCompile>\r
+    <ClCompile Include="clk\clk_command_processor.cpp">\r
       <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
+      <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
     </ClCompile>\r
     <ClCompile Include="osc\oscpack\OscOutboundPacketStream.cpp">\r
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>\r
       <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
       <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
     </ClCompile>\r
+    <ClCompile Include="util\strategy_adapters.cpp">\r
+      <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
+      <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../StdAfx.h</PrecompiledHeaderFile>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <PropertyGroup Label="Globals">\r
     <ProjectGuid>{2040B361-1FB6-488E-84A5-38A580DA90DE}</ProjectGuid>\r
index 77b575a32e687230b16f22e9a070846146cd9c31..21f469fa208639f86da5968afaecb6cea03d673f 100644 (file)
@@ -51,9 +51,6 @@
     <ClInclude Include="clk\CLKProtocolStrategy.h">\r
       <Filter>source\clk</Filter>\r
     </ClInclude>\r
-    <ClInclude Include="clk\CLKCommand.h">\r
-      <Filter>source\clk</Filter>\r
-    </ClInclude>\r
     <ClInclude Include="util\ClientInfo.h">\r
       <Filter>source\util</Filter>\r
     </ClInclude>\r
     <ClInclude Include="osc\server.h">\r
       <Filter>source\osc</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="clk\clk_command_processor.h">\r
+      <Filter>source\clk</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="clk\clk_commands.h">\r
+      <Filter>source\clk</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="util\protocol_strategy.h">\r
+      <Filter>source\util</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="util\strategy_adapters.h">\r
+      <Filter>source\util</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClCompile Include="amcp\AMCPCommandQueue.cpp">\r
     <ClCompile Include="cii\CIIProtocolStrategy.cpp">\r
       <Filter>source\cii</Filter>\r
     </ClCompile>\r
-    <ClCompile Include="clk\CLKCommand.cpp">\r
-      <Filter>source\clk</Filter>\r
-    </ClCompile>\r
     <ClCompile Include="clk\CLKProtocolStrategy.cpp">\r
       <Filter>source\clk</Filter>\r
     </ClCompile>\r
     <ClCompile Include="osc\server.cpp">\r
       <Filter>source\osc</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="clk\clk_command_processor.cpp">\r
+      <Filter>source\clk</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="clk\clk_commands.cpp">\r
+      <Filter>source\clk</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="util\strategy_adapters.cpp">\r
+      <Filter>source\util</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file
index 97c5fcfd4ecac5baa63aba85491bb5c789eba54c..2552d78696da343f0e78889c04b69a3e613fad0c 100644 (file)
 
 #include "AsyncEventServer.h"
 
-#include "ProtocolStrategy.h"
-
 #include <algorithm>
 #include <array>
 #include <string>
 #include <set>
-#include <vector>
+#include <memory>
 
 #include <boost/asio.hpp>
 #include <boost/thread.hpp>
 #include <boost/lexical_cast.hpp>
-#include <boost/locale.hpp>
-#include <boost/algorithm/string/split.hpp>
 
 using boost::asio::ip::tcp;
 
@@ -45,18 +41,18 @@ class connection;
 
 typedef std::set<spl::shared_ptr<connection>> connection_set;
 
-class connection : public spl::enable_shared_from_this<connection>, public ClientInfo
+class connection : public spl::enable_shared_from_this<connection>, public client_connection<char>
 {    
     const spl::shared_ptr<tcp::socket>                 socket_; 
        const spl::shared_ptr<connection_set>           connection_set_;
        const std::wstring                                                      name_;
-       const spl::shared_ptr<IProtocolStrategy>        protocol_;
+       protocol_strategy_factory<char>::ptr            protocol_factory_;
+       std::shared_ptr<protocol_strategy<char>>        protocol_;
 
        std::array<char, 32768>                                         data_;
-       std::string                                                                     input_;
 
 public:
-    static spl::shared_ptr<connection> create(spl::shared_ptr<tcp::socket> socket, const ProtocolStrategyPtr& protocol, spl::shared_ptr<connection_set> connection_set)
+    static spl::shared_ptr<connection> create(spl::shared_ptr<tcp::socket> socket, const protocol_strategy_factory<char>::ptr& protocol, spl::shared_ptr<connection_set> connection_set)
        {
                spl::shared_ptr<connection> con(new connection(std::move(socket), std::move(protocol), std::move(connection_set)));
                con->read_some();
@@ -70,12 +66,12 @@ public:
        
        /* ClientInfo */
        
-       virtual void Send(const std::wstring& data)
+       virtual void send(std::string&& data)
        {
-               write_some(data);
+               write_some(std::move(data));
        }
 
-       virtual void Disconnect()
+       virtual void disconnect()
        {
                stop();
        }
@@ -97,14 +93,22 @@ public:
        }
 
 private:
-    connection(const spl::shared_ptr<tcp::socket>& socket, const ProtocolStrategyPtr& protocol, const spl::shared_ptr<connection_set>& connection_set) 
+    connection(const spl::shared_ptr<tcp::socket>& socket, const protocol_strategy_factory<char>::ptr& protocol, const spl::shared_ptr<connection_set>& connection_set) 
                : socket_(socket)
                , name_((socket_->is_open() ? u16(socket_->local_endpoint().address().to_string() + ":" + boost::lexical_cast<std::string>(socket_->local_endpoint().port())) : L"no-address"))
                , connection_set_(connection_set)
-               , protocol_(protocol)
+               , protocol_factory_(protocol)
        {
                CASPAR_LOG(info) << print() << L" Connected.";
     }
+
+       protocol_strategy<char>& protocol()
+       {
+               if (!protocol_)
+                       protocol_ = protocol_factory_->create(shared_from_this());
+
+               return *protocol_;
+       }
                        
     void handle_read(const boost::system::error_code& error, size_t bytes_transferred) 
        {               
@@ -112,21 +116,11 @@ private:
                {
                        try
                        {
-                               CASPAR_LOG(trace) << print() << L" Received: " << u16(std::string(data_.begin(), data_.begin() + bytes_transferred));
-
-                               input_.append(data_.begin(), data_.begin() + bytes_transferred);
-                               
-                               std::vector<std::string> split;
-                               boost::iter_split(split, input_, boost::algorithm::first_finder("\r\n"));
-                               
-                               input_ = std::move(split.back());
-                               split.pop_back();
-
-                               BOOST_FOREACH(auto cmd, split)
-                               {
-                                       auto u16cmd = boost::locale::conv::to_utf<wchar_t>(cmd, protocol_->GetCodepage()) + L"\r\n";
-                                       protocol_->Parse(u16cmd.data(), static_cast<int>(u16cmd.size()), shared_from_this());
-                               }
+                               std::string data(data_.begin(), data_.begin() + bytes_transferred);
+
+                               CASPAR_LOG(trace) << print() << L" Received: " << u16(data);
+
+                               protocol().parse(data);
                        }
                        catch(...)
                        {
@@ -152,9 +146,9 @@ private:
                socket_->async_read_some(boost::asio::buffer(data_.data(), data_.size()), std::bind(&connection::handle_read, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
        }
        
-       void write_some(const std::wstring& data)
+       void write_some(std::string&& data)
        {
-               auto str = spl::make_shared<std::string>(boost::locale::conv::from_utf<wchar_t>(data, protocol_->GetCodepage()));
+               auto str = spl::make_shared<std::string>(std::move(data));
                socket_->async_write_some(boost::asio::buffer(str->data(), str->size()), std::bind(&connection::handle_write, shared_from_this(), str, std::placeholders::_1, std::placeholders::_2));
        }
 };
@@ -163,11 +157,11 @@ struct AsyncEventServer::implementation
 {
        boost::asio::io_service                                 service_;
        tcp::acceptor                                                   acceptor_;
-       spl::shared_ptr<IProtocolStrategy>              protocol_;
+       protocol_strategy_factory<char>::ptr    protocol_;
        spl::shared_ptr<connection_set>                 connection_set_;
        boost::thread                                                   thread_;
 
-       implementation(const spl::shared_ptr<IProtocolStrategy>& protocol, unsigned short port)
+       implementation(const protocol_strategy_factory<char>::ptr& protocol, unsigned short port)
                : acceptor_(service_, tcp::endpoint(tcp::v4(), port))
                , protocol_(protocol)
                , thread_(std::bind(&boost::asio::io_service::run, &service_))
@@ -214,6 +208,14 @@ struct AsyncEventServer::implementation
     }
 };
 
-AsyncEventServer::AsyncEventServer(const spl::shared_ptr<IProtocolStrategy>& protocol, unsigned short port) : impl_(new implementation(protocol, port)){}
-AsyncEventServer::~AsyncEventServer(){}
+AsyncEventServer::AsyncEventServer(
+               const protocol_strategy_factory<char>::ptr& protocol, unsigned short port)
+       : impl_(new implementation(protocol, port))
+{
+}
+
+AsyncEventServer::~AsyncEventServer()
+{
+}
+
 }}
\ No newline at end of file
index 1212b03fb1a3b91c21a3b7985c19f6d417eeca52..ace053a6a8af88bfc548310e8d2ebf8947f8c891 100644 (file)
 //////////////////////////////////////////////////////////////////////
 #pragma once
 
-#include <common/memory.h>
+#include "protocol_strategy.h"
 
 namespace caspar { namespace IO {
 
-class IProtocolStrategy;
-       
 class AsyncEventServer
 {
 public:
-       explicit AsyncEventServer(const spl::shared_ptr<IProtocolStrategy>& protocol, unsigned short port);
+       explicit AsyncEventServer(const protocol_strategy_factory<char>::ptr& protocol, unsigned short port);
        ~AsyncEventServer();
 private:
        struct implementation;
index 687d22a235f51ed8a50347b9fb7aa8a959974379..73bc2990cf16c859c3fe73f291777139a405d894 100644 (file)
@@ -22,7 +22,7 @@
  #pragma once
 
 #include <string>
-#include "clientInfo.h"
+#include "ClientInfo.h"
 
 namespace caspar { namespace IO {
 
diff --git a/protocol/util/protocol_strategy.h b/protocol/util/protocol_strategy.h
new file mode 100644 (file)
index 0000000..e4c016d
--- /dev/null
@@ -0,0 +1,89 @@
+/*\r
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
+*\r
+* This file is part of CasparCG (www.casparcg.com).\r
+*\r
+* CasparCG is free software: you can redistribute it and/or modify\r
+* it under the terms of the GNU General Public License as published by\r
+* the Free Software Foundation, either version 3 of the License, or\r
+* (at your option) any later version.\r
+*\r
+* CasparCG is distributed in the hope that it will be useful,\r
+* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+* GNU General Public License for more details.\r
+*\r
+* You should have received a copy of the GNU General Public License\r
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
+*\r
+* Author: Helge Norberg, helge.norberg@svt.se\r
+*/\r
+\r
+#pragma once\r
+\r
+#include <string>\r
+\r
+#include <common/memory.h>\r
+\r
+namespace caspar { namespace IO {\r
+\r
+/**\r
+ * A protocol strategy handles a single client connection. A client_connection\r
+ * instance is needed in order to send data to the client.\r
+ */\r
+template<class CharT>\r
+class protocol_strategy\r
+{\r
+public:\r
+       typedef spl::shared_ptr<protocol_strategy<CharT>> ptr;\r
+\r
+       virtual ~protocol_strategy() { }\r
+\r
+       /**\r
+        * Parse some data received. If used directly by the async event server,\r
+        * then the data will be what was received from the TCP/IP stack, but if\r
+        * a delimiter based protocol is used, delimiter_based_chunking_strategy\r
+        * can be used to ensure that the strategy implementation is only\r
+        * provided complete messages.\r
+        *\r
+        * @param data The data received.\r
+        */\r
+       virtual void parse(const std::basic_string<CharT>& data) = 0;\r
+};\r
+\r
+/**\r
+ * A handle for a protocol_strategy to use when interacting with the client.\r
+ */\r
+template<class CharT>\r
+class client_connection\r
+{\r
+public:\r
+       typedef spl::shared_ptr<client_connection<CharT>> ptr;\r
+\r
+       virtual ~client_connection() { }\r
+\r
+       virtual void send(std::basic_string<CharT>&& data) = 0;\r
+       virtual void disconnect() = 0;\r
+       virtual std::wstring print() const = 0;\r
+};\r
+\r
+/**\r
+ * Creates unique instances of protocol_strategy implementations.\r
+ *\r
+ * Each async event server will have one instance of this factory, but create\r
+ * unique protocol_strategy<char> instances for each connected client.\r
+ *\r
+ * Any shared state between client interactions could be held in the factory.\r
+ */\r
+template<class CharT>\r
+class protocol_strategy_factory\r
+{\r
+public:\r
+       typedef spl::shared_ptr<protocol_strategy_factory<CharT>> ptr;\r
+\r
+       virtual ~protocol_strategy_factory() { }\r
+       virtual typename protocol_strategy<CharT>::ptr create(\r
+               const typename client_connection<CharT>::ptr& client_connection) = 0;\r
+};\r
+\r
+}}\r
diff --git a/protocol/util/strategy_adapters.cpp b/protocol/util/strategy_adapters.cpp
new file mode 100644 (file)
index 0000000..c4ded52
--- /dev/null
@@ -0,0 +1,165 @@
+/*\r
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
+*\r
+* This file is part of CasparCG (www.casparcg.com).\r
+*\r
+* CasparCG is free software: you can redistribute it and/or modify\r
+* it under the terms of the GNU General Public License as published by\r
+* the Free Software Foundation, either version 3 of the License, or\r
+* (at your option) any later version.\r
+*\r
+* CasparCG is distributed in the hope that it will be useful,\r
+* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+* GNU General Public License for more details.\r
+*\r
+* You should have received a copy of the GNU General Public License\r
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
+*\r
+* Author: Helge Norberg, helge.norberg@svt.se\r
+*/\r
+\r
+#include "../stdafx.h"\r
+\r
+#include "strategy_adapters.h"\r
+\r
+#include <boost/locale.hpp>\r
+\r
+namespace caspar { namespace IO {\r
+\r
+class to_unicode_adapter : public protocol_strategy<char>\r
+{\r
+       std::string codepage_;\r
+       protocol_strategy<wchar_t>::ptr unicode_strategy_;\r
+public:\r
+       to_unicode_adapter(\r
+                       const std::string& codepage, \r
+                       const protocol_strategy<wchar_t>::ptr& unicode_strategy)\r
+               : codepage_(codepage)\r
+               , unicode_strategy_(unicode_strategy)\r
+       {\r
+       }\r
+\r
+       virtual void parse(const std::basic_string<char>& data)\r
+       {\r
+               auto utf_data = boost::locale::conv::to_utf<wchar_t>(data, codepage_);\r
+\r
+               unicode_strategy_->parse(utf_data);\r
+       }\r
+};\r
+\r
+class from_unicode_client_connection : public client_connection<wchar_t>\r
+{\r
+       client_connection<char>::ptr client_;\r
+       std::string codepage_;\r
+public:\r
+       from_unicode_client_connection(\r
+                       const client_connection<char>::ptr& client, const std::string& codepage)\r
+               : client_(client)\r
+               , codepage_(codepage)\r
+       {\r
+       }\r
+\r
+       virtual void send(std::basic_string<wchar_t>&& data)\r
+       {\r
+               auto str = boost::locale::conv::from_utf<wchar_t>(std::move(data), codepage_);\r
+\r
+               client_->send(std::move(str));\r
+       }\r
+\r
+       virtual void disconnect()\r
+       {\r
+               client_->disconnect();\r
+       }\r
+\r
+       virtual std::wstring print() const\r
+       {\r
+               return client_->print();\r
+       }\r
+};\r
+\r
+to_unicode_adapter_factory::to_unicode_adapter_factory(\r
+               const std::string& codepage, \r
+               const protocol_strategy_factory<wchar_t>::ptr& unicode_strategy_factory)\r
+       : codepage_(codepage)\r
+       , unicode_strategy_factory_(unicode_strategy_factory)\r
+{\r
+}\r
+\r
+protocol_strategy<char>::ptr to_unicode_adapter_factory::create(\r
+       const client_connection<char>::ptr& client_connection)\r
+{\r
+       auto client = spl::make_shared<from_unicode_client_connection>(client_connection, codepage_);\r
+\r
+       return spl::make_shared<to_unicode_adapter>(codepage_, unicode_strategy_factory_->create(client));\r
+}\r
+\r
+class legacy_client_info : public ClientInfo\r
+{\r
+       client_connection<wchar_t>::ptr client_connection_;\r
+public:\r
+       legacy_client_info(const client_connection<wchar_t>::ptr& client_connection)\r
+               : client_connection_(client_connection)\r
+       {\r
+       }\r
+\r
+       virtual void Disconnect()\r
+       {\r
+               client_connection_->disconnect();\r
+       }\r
+\r
+       virtual void Send(const std::wstring& data)\r
+       {\r
+               client_connection_->send(std::wstring(data));\r
+       }\r
+\r
+       virtual std::wstring print() const \r
+       {\r
+               return client_connection_->print();\r
+       }\r
+};\r
+\r
+class legacy_strategy_adapter : public protocol_strategy<wchar_t>\r
+{\r
+       ProtocolStrategyPtr strategy_;\r
+       ClientInfoPtr client_info_;\r
+public:\r
+       legacy_strategy_adapter(\r
+                       const ProtocolStrategyPtr& strategy, \r
+                       const client_connection<wchar_t>::ptr& client_connection)\r
+               : strategy_(strategy)\r
+               , client_info_(std::make_shared<legacy_client_info>(client_connection))\r
+       {\r
+       }\r
+\r
+       virtual void parse(const std::basic_string<wchar_t>& data)\r
+       {\r
+               auto p = data.c_str();\r
+               strategy_->Parse(p, static_cast<int>(data.length()), client_info_);\r
+       }\r
+};\r
+\r
+legacy_strategy_adapter_factory::legacy_strategy_adapter_factory(\r
+               const ProtocolStrategyPtr& strategy)\r
+       : strategy_(strategy)\r
+{\r
+}\r
+\r
+protocol_strategy<wchar_t>::ptr legacy_strategy_adapter_factory::create(\r
+               const client_connection<wchar_t>::ptr& client_connection)\r
+{\r
+       return spl::make_shared<legacy_strategy_adapter>(strategy_, client_connection);\r
+}\r
+\r
+protocol_strategy_factory<char>::ptr wrap_legacy_protocol(\r
+               const std::string& delimiter, \r
+               const ProtocolStrategyPtr& strategy)\r
+{\r
+       return spl::make_shared<delimiter_based_chunking_strategy_factory<char>>(\r
+                       delimiter,\r
+                       spl::make_shared<to_unicode_adapter_factory>(\r
+                                       strategy->GetCodepage(),\r
+                                       spl::make_shared<legacy_strategy_adapter_factory>(strategy)));\r
+}\r
+\r
+}}\r
diff --git a/protocol/util/strategy_adapters.h b/protocol/util/strategy_adapters.h
new file mode 100644 (file)
index 0000000..463d4c1
--- /dev/null
@@ -0,0 +1,149 @@
+/*\r
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
+*\r
+* This file is part of CasparCG (www.casparcg.com).\r
+*\r
+* CasparCG is free software: you can redistribute it and/or modify\r
+* it under the terms of the GNU General Public License as published by\r
+* the Free Software Foundation, either version 3 of the License, or\r
+* (at your option) any later version.\r
+*\r
+* CasparCG is distributed in the hope that it will be useful,\r
+* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+* GNU General Public License for more details.\r
+*\r
+* You should have received a copy of the GNU General Public License\r
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
+*\r
+* Author: Helge Norberg, helge.norberg@svt.se\r
+*/\r
+\r
+#pragma once\r
+\r
+#include <boost/algorithm/string/split.hpp>\r
+\r
+#include "protocol_strategy.h"\r
+#include "ProtocolStrategy.h"\r
+\r
+namespace caspar { namespace IO {\r
+\r
+/**\r
+ * A protocol strategy factory adapter for converting incoming data from a\r
+ * specific codepage to utf-16. The client_connection will do the reversed\r
+ * conversion.\r
+ *\r
+ * The adapter is not safe if the codepage contains multibyte-characters and\r
+ * the data is chunked with potentially incomplete characters, therefore it\r
+ * must be wrapped in an adapter providing complete chunks like\r
+ * delimiter_based_chunking_strategy_factory.\r
+ */\r
+class to_unicode_adapter_factory : public protocol_strategy_factory<char>\r
+{\r
+       std::string codepage_;\r
+       protocol_strategy_factory<wchar_t>::ptr unicode_strategy_factory_;\r
+public:\r
+       to_unicode_adapter_factory(\r
+                       const std::string& codepage, \r
+                       const protocol_strategy_factory<wchar_t>::ptr& unicode_strategy_factory);\r
+\r
+       virtual protocol_strategy<char>::ptr create(\r
+                       const client_connection<char>::ptr& client_connection);\r
+};\r
+\r
+/**\r
+ * Protocol strategy adapter for ensuring that only complete chunks or\r
+ * "packets" are delivered to the wrapped strategy. The chunks are determined\r
+ * by a given delimiter.\r
+ */\r
+template<class CharT>\r
+class delimiter_based_chunking_strategy : public protocol_strategy<CharT>\r
+{\r
+       std::basic_string<CharT> delimiter_;\r
+       std::basic_string<CharT> input_;\r
+       protocol_strategy<CharT>::ptr strategy_;\r
+public:\r
+       delimiter_based_chunking_strategy(\r
+                       const std::basic_string<CharT>& delimiter, \r
+                       const protocol_strategy<CharT>::ptr& strategy)\r
+               : delimiter_(delimiter)\r
+               , strategy_(strategy)\r
+       {\r
+       }\r
+\r
+       virtual void parse(const std::basic_string<char>& data)\r
+       {\r
+               input_ += data;\r
+\r
+               std::vector<std::basic_string<CharT>> split;\r
+               boost::iter_split(split, input_, boost::algorithm::first_finder(delimiter_));\r
+\r
+               input_ = std::move(split.back());\r
+               split.pop_back();\r
+\r
+               BOOST_FOREACH(auto cmd, split)\r
+               {\r
+                       // TODO: perhaps it would be better to not append the delimiter.\r
+                       strategy_->parse(cmd + delimiter_);\r
+               }\r
+       }\r
+};\r
+\r
+template<class CharT>\r
+class delimiter_based_chunking_strategy_factory \r
+       : public protocol_strategy_factory<CharT>\r
+{\r
+       std::basic_string<CharT> delimiter_;\r
+       protocol_strategy_factory<CharT>::ptr strategy_factory_;\r
+public:\r
+       delimiter_based_chunking_strategy_factory(\r
+                       const std::basic_string<CharT>& delimiter, \r
+                       const protocol_strategy_factory<CharT>::ptr& strategy_factory)\r
+               : delimiter_(delimiter)\r
+               , strategy_factory_(strategy_factory)\r
+       {\r
+       }\r
+\r
+       virtual typename protocol_strategy<CharT>::ptr create(\r
+                       const typename client_connection<CharT>::ptr& client_connection)\r
+       {\r
+               return spl::make_shared<delimiter_based_chunking_strategy<CharT>>(\r
+                       delimiter_, strategy_factory_->create(client_connection));\r
+       }\r
+};\r
+\r
+/**\r
+ * Adapts an IProtocolStrategy to be used as a\r
+ * protocol_strategy_factory<wchar_t>.\r
+ *\r
+ * Use wrap_legacy_protocol() to wrap it as a protocol_strategy_factory<char>\r
+ * for use directly by the async event server.\r
+ */\r
+class legacy_strategy_adapter_factory \r
+       : public protocol_strategy_factory<wchar_t>\r
+{\r
+       ProtocolStrategyPtr strategy_;\r
+public:\r
+       legacy_strategy_adapter_factory(const ProtocolStrategyPtr& strategy);\r
+\r
+       virtual protocol_strategy<wchar_t>::ptr create(\r
+                       const client_connection<wchar_t>::ptr& client_connection);\r
+};\r
+\r
+/**\r
+ * Wraps an IProtocolStrategy in a legacy_strategy_adapter_factory, wrapped in\r
+ * a to_unicode_adapter_factory (using the codepage reported by the \r
+ * IProtocolStrategy) wrapped in a delimiter_based_chunking_strategy_factory\r
+ * with the given delimiter string.\r
+ *\r
+ * @param delimiter The delimiter to use to separate messages.\r
+ * @param strategy  The legacy protocol strategy (the same instance will serve\r
+ *                  all connections).\r
+ *\r
+ * @return the adapted strategy.\r
+ */\r
+protocol_strategy_factory<char>::ptr wrap_legacy_protocol(\r
+               const std::string& delimiter, \r
+               const ProtocolStrategyPtr& strategy);\r
+\r
+}}\r
index 8d3cd24c7d818d498e428d3179247ebb8f619f0f..4d2c7cdc0225e4287477943b2ab24785f313b29f 100644 (file)
 #include <common/env.h>
 #include <common/except.h>
 #include <common/utf.h>
+#include <common/memory.h>
 
 #include <core/video_channel.h>
 #include <core/video_format.h>
 #include <core/producer/stage.h>
+#include <core/producer/diag/diag_producer.h>
+#include <core/producer/frame_producer.h>
 #include <core/consumer/output.h>
 
 #include <modules/bluefish/bluefish.h>
@@ -51,6 +54,7 @@
 #include <protocol/cii/CIIProtocolStrategy.h>
 #include <protocol/CLK/CLKProtocolStrategy.h>
 #include <protocol/util/AsyncEventServer.h>
+#include <protocol/util/strategy_adapters.h>
 
 #include <boost/algorithm/string.hpp>
 #include <boost/lexical_cast.hpp>
@@ -95,6 +99,8 @@ struct server::impl : boost::noncopyable
                flash::init();            
                CASPAR_LOG(info) << L"Initialized flash module.";
 
+               core::register_producer_factory(core::create_diag_producer);
+
                setup_channels(env::properties());
                CASPAR_LOG(info) << L"Initialized channels.";
 
@@ -183,17 +189,22 @@ struct server::impl : boost::noncopyable
                }
        }
 
-       spl::shared_ptr<IO::IProtocolStrategy> create_protocol(const std::wstring& name) const
+       IO::protocol_strategy_factory<char>::ptr create_protocol(const std::wstring& name) const
        {
+               using namespace IO;
+
                if(boost::iequals(name, L"AMCP"))
-                       return spl::make_shared<amcp::AMCPProtocolStrategy>(channels_);
+                       return wrap_legacy_protocol("\r\n", spl::make_shared<amcp::AMCPProtocolStrategy>(channels_));
                else if(boost::iequals(name, L"CII"))
-                       return spl::make_shared<cii::CIIProtocolStrategy>(channels_);
+                       return wrap_legacy_protocol("\r\n", spl::make_shared<cii::CIIProtocolStrategy>(channels_));
                else if(boost::iequals(name, L"CLOCK"))
-                       return spl::make_shared<CLK::CLKProtocolStrategy>(channels_);
+                       return spl::make_shared<to_unicode_adapter_factory>(
+                                       "ISO-8859-1",
+                                       spl::make_shared<CLK::clk_protocol_strategy_factory>(channels_));
                
                CASPAR_THROW_EXCEPTION(caspar_exception() << arg_name_info(L"name") << arg_value_info(name) << msg_info(L"Invalid protocol"));
        }
+
 };
 
 server::server() : impl_(new impl()){}