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); + }; }