From eaadd3c8f505f8f1c0ae3cadae5d42fa203aa4ef Mon Sep 17 00:00:00 2001 From: Peter Kwan Date: Sun, 20 Aug 2017 00:37:32 +0100 Subject: [PATCH] Work in progress Rewriting Service. Priority handling will be done in PacketMag. Service will set up and insert all packet types. It will also send events to the packet sources so that they know when they can proceed, --- configure.cpp | 28 ++++---- mag.cpp | 14 ++-- packetmag.cpp | 180 +++++++++++++++++++++++++++++++++++++++++++++-- packetmag.h | 27 ++++--- packetsource.cpp | 6 +- packetsource.h | 5 +- service.cpp | 40 ++++++++--- service.h | 20 +++++- 8 files changed, 269 insertions(+), 51 deletions(-) diff --git a/configure.cpp b/configure.cpp index 6f90c5c..523fb65 100644 --- a/configure.cpp +++ b/configure.cpp @@ -6,7 +6,7 @@ using namespace ttx; int Configure::DirExists(char *path){ struct stat info; - + if(stat(path, &info ) != 0) return 0; else if(info.st_mode & S_IFDIR) @@ -16,7 +16,7 @@ int Configure::DirExists(char *path){ } Configure::Configure(int argc, char** argv) : - + // settings for generation of packet 8/30 _multiplexedSignalFlag(0), _initialMag(1), @@ -28,15 +28,15 @@ Configure::Configure(int argc, char** argv) : std::cerr << "[Configure::Configure] Started" << std::endl; strncpy(_configFile,CONFIGFILE,MAXPATH-1); #ifdef _WIN32 - strncpy(_pageDir,"i:\\temp\\teletext",MAXPATH-1); + strncpy(_pageDir,"j:\\temp\\teletext",MAXPATH-1); // Hard wired for Peter's PC #else strcpy(_pageDir,"/home/pi/teletext"); #endif // This is where the default header template is defined. _headerTemplate = "TEEFAX 1 %%# %%a %d %%b" "\x03" "%H:%M.%S"; - + _rowAdaptive = false; - + //Scan the command line for overriding the pages file. //std::cerr << "[Configure::Configure] Parameters=" << argc << " " << std::endl; if (argc>1) @@ -54,7 +54,7 @@ Configure::Configure(int argc, char** argv) : std::cerr << "[Configure::Configure] " << _pageDir << " does not exist or is not a directory" << std::endl; exit(EXIT_FAILURE); } - + /// @ scan for overriding the configuration file std::cerr << "[Configure::Configure] Pages directory is " << _pageDir << std::endl; std::cerr << "[Configure::Configure] Config file is " << _configFile << std::endl; @@ -75,14 +75,14 @@ Configure::~Configure() int Configure::LoadConfigFile(std::string filename) { std::ifstream filein(filename.c_str()); // Open the file - + std::vector::iterator iter; // these are all the valid strings for config lines std::vector nameStrings{"header_template", "initial_teletext_page", "row_adaptive_mode"}; - + if (filein.is_open()){ std::cerr << "[Configure::LoadConfigFile] opened " << filename << std::endl; - + std::string line; std::string name; std::string value; @@ -91,7 +91,7 @@ int Configure::LoadConfigFile(std::string filename) /// todo: parsing! std::size_t delim = line.find("=", 0); int error = 0; - + if (delim != std::string::npos){ name = line.substr(0, delim); value = line.substr(delim + 1); @@ -102,7 +102,7 @@ int Configure::LoadConfigFile(std::string filename) case 0: // header_template _headerTemplate.assign(value, 0, 32); break; - + case 1: // initial_teletext_page if (value.size() >= 3){ size_t idx; @@ -138,10 +138,10 @@ int Configure::LoadConfigFile(std::string filename) _initialMag = magpage / 0x100; _initialPage = magpage % 0x100; break; - } + } error = 1; break; - + case 2: // row_adaptive_mode if (!value.compare("true")){ _rowAdaptive = true; @@ -166,4 +166,4 @@ int Configure::LoadConfigFile(std::string filename) std::cerr << "[Configure::LoadConfigFile] open failed" << std::endl; return -1; } -} \ No newline at end of file +} diff --git a/mag.cpp b/mag.cpp index 9c7ee30..9dab238 100644 --- a/mag.cpp +++ b/mag.cpp @@ -1,3 +1,5 @@ +/// @todo THIS MODULE WILL BE REDUNDANT AUG 2017 + #include "mag.h" using namespace vbit; @@ -60,7 +62,7 @@ Packet* Mag::GetPacket(Packet* p) * 6) However, before iterating in step 2, do this every second: Look at the carousel list and for each page decrement their timers. * When a page reaches 0 then it is taken as the next page, and its timer reset. */ - + /* pages can be have varying packet codings for X/1 to X/25. Pages using the standard coding we want to send X/26/0 to X/26/15 first, then X/1 to X/25 afterwards. For pages which carry data not for display in packets X/1 to X/25 however we must send these first (so the packets including any X/26 are all sent in sequential order of packet and designation code @@ -197,16 +199,16 @@ Packet* Mag::GetPacket(Packet* p) //p->Parity(13); // don't apply parity here it will screw up the template. parity for the header is done by tx() later assert(p!=NULL); - + links=_page->GetLinkSet(); if ((links[0] & links[1] & links[2] & links[3] & links[4] & links[5]) != 0x8FF){ // only create if links were initialised _state=STATE_FASTEXT; // a non zero FL row will override an OL,27 row - } else { + } else { _lastTxt=_page->GetTxRow(27); // Get _lastTxt ready for packet 27 processing _state=STATE_PACKET27; } break; - + case STATE_FASTEXT: p->SetMRAG(_magNumber,27); links=_page->GetLinkSet(); @@ -231,7 +233,7 @@ Packet* Mag::GetPacket(Packet* p) { //std::cerr << "Packet 28 length=" << _lastTxt->GetLine().length() << std::endl; //_lastTxt->Dump(); - + p->SetRow(_magNumber, 28, _lastTxt->GetLine(), CODING_13_TRIPLETS); _lastTxt=_lastTxt->GetNextLine(); break; @@ -240,7 +242,7 @@ Packet* Mag::GetPacket(Packet* p) { // create X/28/0 packet for pages which have a region set with RE in file // it is important that pages with X/28/0,2,3,4 packets don't set a region otherwise an extra X/28/0 will be generated. TTXPage::SetRow sets the region to 0 for these packets just in case. - + // this could almost certainly be done more efficiently but it's quite confusing and this is more readable for when it all goes wrong. std::string val = "@@@tGpCuW@twwCpRA`UBWwDsWwuwwwUwWwuWwE@@"; // default X/28/0 packet int region = _page->GetRegion(); diff --git a/packetmag.cpp b/packetmag.cpp index 6dd6e71..63abb41 100644 --- a/packetmag.cpp +++ b/packetmag.cpp @@ -12,7 +12,11 @@ PacketMag::PacketMag(uint8_t mag, std::list* pageSet, ttx::Config _page(NULL), _magNumber(mag), _priority(priority), - _priorityCount(priority) + _priorityCount(priority), + _headerFlag(false), + _state(PACKETSTATE_HEADER), + _thisRow(0), + _lastTxt(NULL) { //ctor if (_pageSet->size()>0) @@ -32,10 +36,21 @@ PacketMag::~PacketMag() } // @todo Invent a packet sequencer similar to mag.cpp which this will replace -Packet* PacketMag::GetPacket() +Packet* PacketMag::GetPacket(Packet* p) { + int thisPageNum; + unsigned int thisSubcode; + int thisStatus; + int* links=NULL; + static vbit::Packet* filler=new Packet(8,25," "); // filler + // We should only call GetPacket if IsReady has returned true + if (!IsReady()) + { + std::cerr << "[PacketMag::GetPacket] Packet not ready. This must not happen" << std::endl; + exit(0); + } // If there is no page, we should send a filler if (_pageSet->size()<1) @@ -43,9 +58,164 @@ Packet* PacketMag::GetPacket() return filler; } - // If we send a header we go into a wait state - ClearEvent(EVENT_FIELD); // @todo Only when we send the header - return filler; // Dummy return for now + switch (_state) + { + case PACKETSTATE_HEADER: // Start to send out a new page, which may be a simple page or one of a carousel + ClearEvent(EVENT_FIELD); // This will suspend all packets until the next field. + + _page=_carousel->nextCarousel(); // The next carousel page + + // But before that, do some housekeeping + + // Is this page deleted? + if (_page && _page->GetStatusFlag()==TTXPageStream::MARKED) + { + _carousel->deletePage(_page); + _page=NULL; + } + + if (_page) // Carousel? Step to the next subpage + { + //_outp("c"); + _page->StepNextSubpage(); + //std::cerr << "[Mag::GetPacket] Header thisSubcode=" << std::hex << _page->GetCarouselPage()->GetSubCode() << std::endl; + + } + else // No carousel? Take the next page in the main sequence + { + if (_it==_pageSet->end()) + { + std::cerr << "This can not happen" << std::endl; + exit(0); + } + ++_it; + if (_it==_pageSet->end()) + { + _it=_pageSet->begin(); + } + // Get pointer to the page we are sending + // todo: Find a way to skip carousels without going into an infinite loop + _page=&*_it; + // If it is marked for deletion, then remove it and send a filler instead. + if (_page->GetStatusFlag()==TTXPageStream::MARKED) + { + _pageSet->remove(*(_it++)); + _page=NULL; + return filler; + // Stays in HEADER mode so that we run this again + } + if (_page->IsCarousel() && _page->GetCarouselFlag()) // Don't let registered carousel pages into the main page sequence + { + std::cerr << "This can not happen" << std::endl; + exit(0); + // Page is a carousel. This can not happen + //_page=NULL; + //return NULL; + } + } + _thisRow=0; + + // When a single page is changed into a carousel + if (_page->IsCarousel() != _page->GetCarouselFlag()) + { + _page->SetCarouselFlag(_page->IsCarousel()); + if (_page->IsCarousel()) + { + // std::cerr << "This page has become a carousel. Add it to the list" << std::endl; + _carousel->addPage(_page); + } + else + { + // @todo Implement this + //std::cerr << "@todo This page has no longer a carousel. Remove it from the list" << std::endl; + //exit(3); // + } + } + + // Assemble the header. (we can simplify this code or leave it for the optimiser) + thisPageNum=_page->GetPageNumber(); + thisPageNum=(thisPageNum/0x100) % 0x100; // Remove this line for Continuous Random Acquisition of Pages. + if (_page->IsCarousel()) + { + thisSubcode=_page->GetCarouselPage()->GetSubCode(); + } + else + { + thisSubcode=_page->GetSubCode(); + } + + thisStatus=_page->GetPageStatus(); + // p=new Packet(); + p->Header(_magNumber,thisPageNum,thisSubcode,thisStatus);// loads of stuff to do here! + + p->HeaderText(_configure->GetHeaderTemplate()); // Placeholder 32 characters. This gets replaced later + + + //p->Parity(13); // don't apply parity here it will screw up the template. parity for the header is done by tx() later + assert(p!=NULL); + + links=_page->GetLinkSet(); + if ((links[0] & links[1] & links[2] & links[3] & links[4] & links[5]) != 0x8FF){ // only create if links were initialised + _state=PACKETSTATE_FASTEXT; // a non zero FL row will override an OL,27 row + } else { + _lastTxt=_page->GetTxRow(27); // Get _lastTxt ready for packet 27 processing + _state=PACKETSTATE_PACKET27; + } + break; + case PACKETSTATE_TEXTROW: + // Find the next row that isn't NULL + for (_thisRow++;_thisRow<26;_thisRow++) + { + _lastTxt=_page->GetTxRow(_thisRow); + if (_lastTxt!=NULL) + break; + } + // Didn't find? End of this page. + if (_thisRow>25 || _lastTxt==NULL) + { + if(_page->GetPageCoding() == CODING_7BIT_TEXT){ + // if this is a normal page we've finished + p=NULL; + _state=PACKETSTATE_HEADER; + _thisRow=0; + //_outp("H"); + } else { + // otherwise go on to X/26 + _lastTxt=_page->GetTxRow(26); + _state=PACKETSTATE_PACKET26; + } + } + else + { + //_outp("J"); + if (_lastTxt->IsBlank() && _configure->GetRowAdaptive()) // If the row is empty then skip it + { + // std::cerr << "[Mag::GetPacket] Empty row" << std::hex << _page->GetPageNumber() << std::dec << std::endl; + p=NULL; + } + else + { + // Assemble the packet + p->SetRow(_magNumber, _thisRow, _lastTxt->GetLine(), _page->GetPageCoding()); + if (_page->GetPageCoding() == CODING_7BIT_TEXT) + p->Parity(); // only set parity for normal text rows + assert(p->IsHeader()!=true); + } + } + break; + case PACKETSTATE_FASTEXT: + p->SetMRAG(_magNumber,27); + links=_page->GetLinkSet(); + p->Fastext(links,_magNumber); + _lastTxt=_page->GetTxRow(28); // Get _lastTxt ready for packet 28 processing + _state=PACKETSTATE_PACKET28; + break; + default: + _state=PACKETSTATE_HEADER;// For now, do the next page + // Other packets that Alistair will want implemented go here + } + + return p; // } /** Is there a packet ready to go? diff --git a/packetmag.h b/packetmag.h index b0e9c0a..ae172a2 100644 --- a/packetmag.h +++ b/packetmag.h @@ -9,6 +9,7 @@ namespace vbit { +enum PacketState {PACKETSTATE_HEADER, PACKETSTATE_FASTEXT, PACKETSTATE_PACKET26, PACKETSTATE_PACKET27, PACKETSTATE_PACKET28, PACKETSTATE_TEXTROW}; class PacketMag : public PacketSource { @@ -21,7 +22,7 @@ class PacketMag : public PacketSource /** Get the next packet * @return The next packet OR if IsReady() would return false then a filler packet */ - Packet* GetPacket() override; + Packet* GetPacket(Packet* p) override; bool IsReady(); @@ -29,15 +30,21 @@ class PacketMag : public PacketSource protected: private: - std::list* _pageSet; //!< Member variable "_pageSet" - ttx::Configure* _configure; - TTXPageStream* _page; //!< The current page being output - int _magNumber; //!< The number of this magazine. (where 0 is mag 8) - uint8_t _priority; //!< Priority of transmission where 1 is highest - - std::list::iterator _it; - Carousel* _carousel; - uint8_t _priorityCount; // Controls transmission priority + std::list* _pageSet; //!< Member variable "_pageSet" + ttx::Configure* _configure; + TTXPageStream* _page; //!< The current page being output + int _magNumber; //!< The number of this magazine. (where 0 is mag 8) + uint8_t _priority; //!< Priority of transmission where 1 is highest + + std::list::iterator _it; + Carousel* _carousel; + uint8_t _priorityCount; /// Controls transmission priority + bool _headerFlag; /// True if the last packet was a header + PacketState _state; /// State machine to sequence packet types + uint8_t _thisRow; // The current line that we are outputting + TTXLine* _lastTxt; // The text of the last row that we fetched. Used for enhanced packets + + }; } diff --git a/packetsource.cpp b/packetsource.cpp index 4ea36a7..24ea6bc 100644 --- a/packetsource.cpp +++ b/packetsource.cpp @@ -22,10 +22,10 @@ PacketSource::~PacketSource() /** Need to override this function in a child class * The implementation here is useless so don't call it. In fact I should remove it to avoid confusion. */ -Packet* PacketSource::GetPacket() +Packet* PacketSource::GetPacket(Packet* p) { - Packet* pkt=new Packet(8,25," "); - return pkt; + p=new Packet(8,25," "); + return p; } void PacketSource::SetEvent(Event event) diff --git a/packetsource.h b/packetsource.h index 6710605..905ddf6 100644 --- a/packetsource.h +++ b/packetsource.h @@ -48,7 +48,8 @@ #include -namespace vbit{ +namespace vbit +{ /** @brief Events are used to trigger packet sources so that they may proceed * @description Different packet sources use different timing schemes. @@ -86,7 +87,7 @@ class PacketSource /** Get the next packet * @return The next packet OR if IsReady() would return false then a filler packet */ - virtual Packet* GetPacket()=0; + virtual Packet* GetPacket(Packet* p)=0; /** Is there a packet ready to go? */ virtual bool IsReady(){return _readyFlag;}; diff --git a/service.cpp b/service.cpp index 41bb69b..902f14f 100644 --- a/service.cpp +++ b/service.cpp @@ -3,6 +3,7 @@ #include "service.h" using namespace ttx; +using namespace vbit; Service::Service() { @@ -11,22 +12,38 @@ Service::Service() Service::Service(Configure *configure, PageList *pageList) : _configure(configure),_pageList(pageList) { + // @todo Put priority into config and add commands to allow updates. + uint8_t priority[8]={5,3,3,6,3,3,5,9}; // 1=High priority,9=low. Note: priority[0] is mag 8 + + vbit::Mag **magList=_pageList->GetMagazines(); + // Register all the packet sources + for (uint8_t mag=0;mag<8;mag++) + { + vbit::Mag* m=magList[mag]; + std::list* p=m->Get_pageSet(); + _register(new PacketMag(mag,p,_configure,priority[mag])); + } + // @todo Add packet sources for subtitles, databroadcast and packet 830 } Service::~Service() { } +void Service::_register(PacketSource *src) +{ + _Sources.push_front(src); +} + int Service::run() { - // @todo Put priority into config and add commands to allow updates. - uint8_t priority[STREAMS]={5,3,3,6,3,3,5,9,1}; // 1=High priority,9=low. Note: priority[0] is mag 8, while priority mag[8] is the newfor stream! - uint8_t priorityCount[STREAMS]; + std::cerr << "[Service::worker]This is the worker process" << std::endl; uint8_t nmag=1; vbit::Mag **mag; // Pointer to magazines array vbit::Mag *pMag; // Pointer to the magazine that we are working on + uint8_t priorityCount[STREAMS]; // Will be handled by the packet sources themselves. + uint8_t hold[STREAMS]; /// If hold is set then the magazine can not be sent until the next field - std::cerr << "[Service::worker]This is the worker process" << std::endl; vbit::Packet* pkt=new vbit::Packet(8,25," "); // This just allocates storage. @@ -36,18 +53,25 @@ int Service::run() // Initialise the priority counts + /* for (uint8_t i=0;iGetMagazines(); + // mag=_pageList->GetMagazines(); std::cerr << "[Service::worker]Loop starts" << std::endl; while(1) // normal { + // If counters (or other trigger) causes an event then send the events + // Iterate through the packet sources + // Check if there is a packet available + // If there is then send it + // update the counters. // Find the next magazine to put out. // We decrement the priority count for each magazine until one reaches 0. @@ -68,14 +92,14 @@ int Service::run() { if (!hold[nmag]) // Not in hold { - pMag->GetPacket(pkt); + pMag->GetPacket(pkt); // YEAH. Don't do this!!!! bool isHeader=false; if (pkt!=NULL) // How could this be NULL? After the last packet of a page has been sent. { isHeader=pMag->GetHeaderFlag(); // pkt->IsHeader(); - + std::cout.write(pkt->tx(), 42); // Transmit the packet - using cout.write to ensure writing 42 bytes even if it contains a null. - + // Was the last packet a header? If so we need to go into hold if (isHeader) { diff --git a/service.h b/service.h index 183760c..94c5052 100644 --- a/service.h +++ b/service.h @@ -5,19 +5,28 @@ #include #include #include +#include #include "configure.h" #include "pagelist.h" #include "packet.h" -#include "mag.h" +#include "mag.h" // @todo THIS WILL BE REDUNDANT +#include "packetsource.h" +#include "packetmag.h" /// Eight magazines and subtitles (maybe other packets too) #define STREAMS 9 namespace ttx { -/** A Service has a name, source folder, a header format. - * It loads in pages from the source folder and generates a teletext stream on stdout. + +/** A Service creates a teletext stream from packet sources. + * Packet sources are magazines, subtitles, Packet 830 and databroadcast. + * Service: + * Instances the packet sources + * Sends them timing events (cues for field timing etc.) + * Polls the packet sources for packets to send + * Sends the packets. * */ class Service @@ -36,9 +45,14 @@ class Service */ int run(); private: + // Member variables Configure* _configure; /// Member reference to the configuration settings PageList* _pageList; /// Member reference to the pages list + // Member functions + std::list _Sources; + void _register(vbit::PacketSource *src); + }; }