BlinkenArea - GitList
Repositories
Blog
Wiki
libetherpix
Code
Commits
Branches
Tags
Search
Tree:
90d5ef5
Branches
Tags
master
libetherpix
config_gen
src
main.cpp
fix error messages and formatting
Stefan Schuermans
commited
90d5ef5
at 2018-08-14 11:42:52
main.cpp
Blame
History
Raw
/* * EtherPix config file generator * * Copyright 2010-2017 Stefan Schuermans <stefan schuermans info> * * This program 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, version 3 of the License. * * * This program 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 Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <algorithm> #include <dime/entities/Entity.h> #include <dime/Input.h> #include <dime/Model.h> #include <dime/State.h> #include <fstream> #include <iostream> #include <map> #include <string> #include "chain.h" #include "constants.h" #include "distri.h" #include "layer.h" #include "line.h" #include "pixel.h" #include "point.h" #define SUCCESS (0) // no error #define ERR_USAGE (2) // invalid command line arguments #define ERR_FILE_OPEN (3) // could not open input/output file #define ERR_FILE_READ (4) // could not read input file #define ERR_FILE_PROC (5) // could not process input file #define ERR_PROC (5) // error in processing of objects found in input file // layers of drawing Layer gLayerVideo, gLayerPixel, gLayerNetwork; std::map<unsigned int, Layer> gLayerCables, gLayerDistributors; Layer gLayerCable; /** * @brief get configuration from special layer names * @param[in] model DXF drawing * @param[out] width virtual frame width in pixels * @param[out] height virtual frame height in pixels * @param[out] chains number of outputs / pixel chains per distributor * @param[out] pixels number of pixels per chain * return true for success */ static bool get_config(dimeModel const &model, unsigned int &width, unsigned int &height, unsigned int &chains, unsigned int &pixels) { int layers, layer_no; width = 0; height = 0; chains = 0; pixels = 0; layers = model.getNumLayers(); for (layer_no = 0; layer_no < layers; ++layer_no) { dimeLayer const *layer = model.getLayer(layer_no); std::string strLayerName = layer->getLayerName(); if (strLayerName.substr(0, 10) == "cfg_width ") { if (width != 0) { std::cerr << "duplicate configuration of width" << std::endl; return false; } width = strtoul(strLayerName.substr(10).c_str(), NULL, 0); if (width == 0) { std::cerr << "invalid configuration of width" << std::endl; return false; } } else if (strLayerName.substr(0, 11) == "cfg_height ") { if (height != 0) { std::cerr << "duplicate configuration of height" << std::endl; return false; } height = strtoul(strLayerName.substr(11).c_str(), NULL, 0); if (height == 0) { std::cerr << "invalid configuration of height" << std::endl; return false; } } else if (strLayerName.substr(0, 12) == "cfg_outputs ") { if (chains != 0) { std::cerr << "duplicate configuration of number of outputs per distributor" << std::endl; return false; } chains = strtoul(strLayerName.substr(12).c_str(), NULL, 0); if (chains == 0) { std::cerr << "invalid configuration of number of outputs per distributor" << std::endl; return false; } } else if (strLayerName.substr(0, 11) == "cfg_pixels ") { if (pixels != 0) { std::cerr << "duplicate configuration of number of pixels per output" << std::endl; return false; } pixels = strtoul(strLayerName.substr(11).c_str(), NULL, 0); if (pixels == 0) { std::cerr << "invalid configuration of number of pixels per output" << std::endl; return false; } } } // for layer_no if (width == 0) { std::cerr << "missing configuration of width" << std::endl; return false; } if (height == 0) { std::cerr << "missing configuration of height" << std::endl; return false; } if (chains == 0) { std::cerr << "missing configuration of number of outputs per distributor" << std::endl; return false; } if (pixels == 0) { std::cerr << "missing configuration of number of pixels per output" << std::endl; return false; } std::cout << "configuration:" << std::endl; std::cout << " width: " << width << std::endl; std::cout << " height: " << height << std::endl; std::cout << " outputs: " << chains << std::endl; std::cout << " pixels: " << pixels << std::endl; return true; } /** * @brief enumerate a entity in the DXF file * @param[in] state current state of libdime * @param[in] entity pointer to entity * @param[in] vp_ctx context pointer * @return if to continue enumeration */ static bool cbEntity(const class dimeState * const state, class dimeEntity *entity, void *vp_ctx) { (void)vp_ctx; // get layer std::string strLayerName = entity->getLayerName(); Layer * pLayer; if (strLayerName == "video") { pLayer = &gLayerVideo; } else if (strLayerName == "pixel") { pLayer = &gLayerPixel; } else if (strLayerName == "network") { pLayer = &gLayerNetwork; } else if (strLayerName == "cable") { pLayer = &gLayerCables[0]; } else if (strLayerName.substr(0, 6) == "cable ") { unsigned int no = strtoul(strLayerName.substr(6).c_str(), NULL, 0); pLayer = &gLayerCables[no]; } else if (strLayerName.substr(0, 12) == "distributor ") { unsigned int no = strtoul(strLayerName.substr(12).c_str(), NULL, 0); pLayer = &gLayerDistributors[no]; } else { return true; // unknown layer -> ignore } // get transformation matrix dimeMatrix matrix; state->getMatrix(matrix); // get geometry data dimeArray<dimeVec3f> vertices; dimeArray<int> indices; dimeVec3f extrusionDir; dxfdouble thickness; entity->extractGeometry(vertices, indices, extrusionDir, thickness); // transform geometry data for (int i = 0; i < vertices.count(); ++i) matrix.multMatrixVec(vertices[i]); // build object from geometry data Object *pObject = new Object; for (int i = 1; i < vertices.count(); ++i) { Line *pLine = new Line(Point(vertices[i - 1].x, vertices[i - 1].y), Point(vertices[i].x, vertices[i].y)); if (pLine->length() >= EPSILON) // ignore dots pObject->add(pLine); else { std::cerr << "ignoring dot at " << pLine->mStart.mX << "," << pLine->mStart.mY << std::endl; delete pLine; } } // build object into layer pLayer->build(pObject); return true; } /** * @brief read a DXF file and process objects found in it * @param[in] strDxfFileName name of DXF file * @param[out] width width of logical video frame * @param[out] height height of logical video frame * @param[out] chains number of chains (outputs) per distributor * @param[out] pixels number of pixels in one chain * (i.e. connected to one output) * @return SUCCESS on success, other value on error */ static int readDXF(std::string const &strDxfFileName, unsigned int &width, unsigned int &height, unsigned int &chains, unsigned int &pixels) { // read DXF file dimeInput in; if (!in.setFile(strDxfFileName.c_str())) { std::cerr << "error opening file \"" << strDxfFileName << "\" for reading" << std::endl; return ERR_FILE_OPEN; } dimeModel model; if (!model.read(&in)) { std::cerr << "DXF read error in line " << in.getFilePosition() << " of file \"" << strDxfFileName << "\"" << std::endl; return ERR_FILE_READ; } // get configuration from layers if (! get_config(model, width, height, chains, pixels)) { return ERR_FILE_PROC; } // enumerate all entities model.traverseEntities(cbEntity, NULL); // merge all cable layers std::map<unsigned int, Layer>::iterator itCable; for (itCable = gLayerCables.begin(); itCable != gLayerCables.end(); ++itCable) gLayerCable.merge(&itCable->second); // gLayerCables[] is now empty and will not be used any more // output object count information std::cout << "object counts:" << std::endl; std::cout << " video: " << gLayerVideo.mObjects.size() << std::endl; std::cout << " pixel: " << gLayerPixel.mObjects.size() << std::endl; std::cout << " cable: " << gLayerCable.mObjects.size() << std::endl; std::cout << " network: " << gLayerNetwork.mObjects.size() << std::endl; std::cout << " distributor:" << std::endl; std::map<unsigned int, Layer>::const_iterator itDistri; for (itDistri = gLayerDistributors.begin(); itDistri != gLayerDistributors.end(); ++itDistri) std::cout << " 0x" << std::hex << itDistri->first << std::dec << ": " << itDistri->second.mObjects.size() << std::endl; return SUCCESS; } /** * @brief create a chain * @param[in] chain chain to create * @param[in] pObjCable cable to first pixel * @return SUCCESS on success, other value on error */ static int createChain(Chain &chain, const Object *pObjCable) { // add all pixels const Object *pObjPixel = NULL; while (true) { // find pixels connected to cable std::vector<Layer::Intersection> intersectsPixels; gLayerPixel.getIntersections(pObjCable, intersectsPixels); // remove last pixel from results std::vector<Layer::Intersection>::iterator itPixel; for (itPixel = intersectsPixels.begin(); itPixel != intersectsPixels.end(); ++itPixel) { if (itPixel->pObj == pObjPixel) { itPixel = intersectsPixels.erase(itPixel); if (itPixel == intersectsPixels.end()) break; } } // end of chain if (intersectsPixels.size() < 1) return SUCCESS; // more than one next pixel if (intersectsPixels.size() > 1) { std::cerr << "pixel cable connects multiple pixels (" << intersectsPixels[0].pt.mX << "," << intersectsPixels[0].pt.mY << ")" << std::endl; return ERR_PROC; } // pixel found pObjPixel = intersectsPixels[0].pObj; // add pixel to chain chain.addPixel(Pixel(pObjPixel)); // find cables connected to pixel std::vector<Layer::Intersection> intersectsCables; gLayerCable.getIntersections(pObjPixel, intersectsCables); // remove last cable from results std::vector<Layer::Intersection>::iterator itCable; for (itCable = intersectsCables.begin(); itCable != intersectsCables.end(); ++itCable) { if (itCable->pObj == pObjCable) { itCable = intersectsCables.erase(itCable); if (itCable == intersectsCables.end()) break; } } // end of chain if (intersectsCables.size() < 1) return SUCCESS; // more than one next pixel if (intersectsCables.size() > 1) { std::cerr << "pixel connected to multiple pixel cables (" << intersectsCables[0].pt.mX << "," << intersectsCables[0].pt.mY << ")" << std::endl; return ERR_PROC; } // next cable found pObjCable = intersectsCables[0].pObj; } // while (true) } /// sort helper for distributor/cables intersections static bool intersectsCablesSort(const Layer::Intersection& i1, const Layer::Intersection& i2) { return i1.pt.abs_sq() < i2.pt.abs_sq(); } /** * @brief create a distributor * @param[in] distri distributor to create * @param[in] pLayer layer containing distributor * @param[in] pixels number of pixels per chain * @return SUCCESS on success, other value on error */ static int createDistri(Distri &distri, const Layer *pLayer, unsigned int pixels) { // must have exactly one object if (pLayer->mObjects.size() != 1) { std::cerr << "layer of distributor 0x" << std::hex << distri.getNo() << std::dec << " contains no object or multiple objects" << std::endl; return ERR_PROC; } const Object *pObjDistri = pLayer->mObjects[0]; // get location of network connection std::vector<Layer::Intersection> intersectsNet; gLayerNetwork.getIntersections(pObjDistri, intersectsNet); if (intersectsNet.size() != 1) { std::cerr << "distributor 0x" << std::hex << distri.getNo() << std::dec << "has no network connection or multiple network connections" << std::endl; return ERR_PROC; } Point ptNetwork = intersectsNet[0].pt; // get cables to pixel chains std::vector<Layer::Intersection> intersectsCables; gLayerCable.getIntersections(pObjDistri, intersectsCables); // sort cables according to distance to network std::vector<Layer::Intersection>::iterator itIntersect; for (itIntersect = intersectsCables.begin(); itIntersect != intersectsCables.end(); ++itIntersect) itIntersect->pt -= ptNetwork; std::sort(intersectsCables.begin(), intersectsCables.end(), intersectsCablesSort); // create chains bool err = false; for (itIntersect = intersectsCables.begin(); itIntersect != intersectsCables.end(); ++itIntersect) { distri.addChain(Chain(pixels)); if (createChain(distri.accessLastChain(), itIntersect->pObj) != 0) err = true; } return err ? ERR_PROC : SUCCESS; } /** * @brief make config file object from DXF layers * @param[in] width width of logical video frame * @param[in] height height of logical video frame * @param[in] chains number of chains (outputs) per distributor * @param[in] pixels number of pixels in one chain * (i.e. connected to one output) * @param[out] boundsVideo bounds of video screen object * @param[out] distris distributor objects * @return true on success, false on error */ static bool makeObjects(unsigned int width, unsigned int height, unsigned int chains, unsigned int pixels, Box &boundsVideo, std::vector<Distri> &distris) { // get origin and size of pixels in video gLayerVideo.getBounds(boundsVideo); Point ptPixel0(boundsVideo.mBL.mX, boundsVideo.mTR.mY); Point ptPixelSz = boundsVideo.mTR - boundsVideo.mBL; ptPixelSz.mX /= (double)width; ptPixelSz.mY /= (double)height; ptPixelSz.mY *= -1; std::cout << "video bounds: " << boundsVideo.mBL.mX << "," << boundsVideo.mBL.mY << " " << boundsVideo.mTR.mX << "," << boundsVideo.mTR.mY << std::endl; // create distributors bool ok = true; std::map<unsigned int, Layer>::const_iterator itDistri; for (itDistri = gLayerDistributors.begin(); itDistri != gLayerDistributors.end(); ++itDistri) { distris.push_back(Distri(itDistri->first, chains, pixels)); if (createDistri(distris.back(), &itDistri->second, pixels) != SUCCESS) { ok = false; } } // obtain pixel coordinates std::vector<Distri>::iterator itD; for (itD = distris.begin(); itD != distris.end(); ++itD) if (itD->pixCoord(ptPixel0, ptPixelSz, width, height) != 0) ok = false; // count pixels connected to distributors and compare to total pixels size_t distri_pixels = 0; for (itD = distris.begin(); itD != distris.end(); ++itD) distri_pixels += itD->countPixels(); std::cout << "pixels connected to distributors: " << distri_pixels << std::endl; if (distri_pixels != gLayerPixel.mObjects.size()) { std::cerr << "number of connected pixels (" << distri_pixels << ") does not match total number of pixels (" << gLayerPixel.mObjects.size() << ")" << std::endl; ok = false; } // output extra error message if errors were detected if (! ok) { std::cerr << "ERRORs were found" << std::endl; } return ok; } /** * @brief write config file * @param[in] width width of video in pixels * @param[in] height height of video in pixels * @param[in] distris distributor objects * @param[in] strCfgFileName name of config file * @reutrn SUCCESS on success, other value on error */ static int writeCfg(unsigned int width, unsigned int height, const std::vector<Distri> &distris, const std::string & strCfgFileName) { // open output file std::ofstream strm(strCfgFileName.c_str(), std::ios::out); if (!strm.is_open()) { std::cerr << "could not open \"" << strCfgFileName << "\" for wrinting" << std::endl; return ERR_FILE_OPEN; } // video size strm << "size = " << width << "," << height << std::endl << std::endl; // distributors std::vector<Distri>::const_iterator itD; for (itD = distris.begin(); itD != distris.end(); ++itD) itD->writeDistri(strm); strm << std::endl; // mappings for (itD = distris.begin(); itD != distris.end(); ++itD) itD->writeMapping(strm); strm << std::endl; // pixels for (itD = distris.begin(); itD != distris.end(); ++itD) itD->writePixels(strm); strm << std::endl; strm.close(); return SUCCESS; } /** * @brief write simulator config file * @param[in] distris distributor objects * @param[in] boundsVideo bounds of video screen object * @param[in] strSimCfgFileName name of simulator config file * @reutrn SUCCESS on success, other value on error */ static int writeSimCfg(const std::vector<Distri> &distris, const Box & boundsVideo, const std::string & strSimCfgFileName) { /* make video bounds quadratic in order to keep aspect ratio of simulator image */ Box boundsVideoQuad = boundsVideo.expandQuadratic(); // open output file std::ofstream strm(strSimCfgFileName.c_str(), std::ios::out); if (!strm.is_open()) { std::cerr << "could not open \"" << strSimCfgFileName << "\" for wrinting" << std::endl; return ERR_FILE_OPEN; } // distributors std::vector<Distri>::const_iterator itD; for (itD = distris.begin(); itD != distris.end(); ++itD) itD->writeDistri(strm); strm << std::endl; // mappings for (itD = distris.begin(); itD != distris.end(); ++itD) itD->writeMapping(strm); strm << std::endl; // simulated pixels for (itD = distris.begin(); itD != distris.end(); ++itD) itD->writeSimPixels(strm, boundsVideoQuad); strm << std::endl; strm.close(); return SUCCESS; } /** * @brief main program * @param[in] argc number of parameters * @param[in] argv parameter values * @return SUCCESS on success, other value on error */ int main(int argc, char *argv[]) { // get parameters bool arg_err = 0, bDxfFileName = false, bCfgFileName = false, bSimCfgFileName= false; std::string strDxfFileName, strCfgFileName, strSimCfgFileName; // old usage if (argc == 3) { std::cerr << "warning: old usage, consider using \"-d\" and \"-c\" options" << std::endl; strDxfFileName = argv[1]; bDxfFileName = true; strCfgFileName = argv[2]; bCfgFileName = true; } else { int argi; for (argi = 1; argi < argc; ++argi) { if (strcmp(argv[argi], "-c") == 0) { ++argi; if (argi < argc) { strCfgFileName = argv[argi]; bCfgFileName = true; } else { std::cerr << "error: missing argument for \"-c\"" << std::endl; } } else if (strcmp(argv[argi], "-d") == 0) { ++argi; if (argi < argc) { strDxfFileName = argv[argi]; bDxfFileName = true; } else { std::cerr << "error: missing argument for \"-d\"" << std::endl; } } else if (strcmp(argv[argi], "-s") == 0) { ++argi; if (argi < argc) { strSimCfgFileName = argv[argi]; bSimCfgFileName = true; } else { std::cerr << "error: missing argument for \"-s\"" << std::endl; } } else { std::cerr << "error: unknown option \"" << argv[argi] << "\", call without arguments for usage" << std::endl; arg_err = true; } } // for argi if (! bDxfFileName) { std::cerr << "error: missing input drawing (\"-d\")" << std::endl; arg_err = true; } } // output help on error if (arg_err) { std::cerr << "EtherPix config file generator " << ETPCG_VER_MAJ << "." << ETPCG_VER_MIN << "." << ETPCG_VER_REV << std::endl << "usage: " << argv[0] << " [options]" << std::endl << "options: -c <config.etp> configuration file (output)" << std::endl << " -d <drawing.dxf> schematic drawing (input)" << std::endl << " -s <sim_config.etps> simulator configuration (output)" << std::endl << "old usage: " << argv[0] << " <schematic_drawing.dxf> <config.etp>" << std::endl; return ERR_USAGE; } // read DXF file unsigned int width, height, chains, pixels; int ret = readDXF(strDxfFileName, width, height, chains, pixels); if (ret != SUCCESS) { return ret; } // make config file objects from DXF layers Box boundsVideo; std::vector<Distri> distris; if (! makeObjects(width, height, chains, pixels, boundsVideo, distris)) { return ERR_PROC; } // write config file if (bCfgFileName) { ret = writeCfg(width, height, distris, strCfgFileName); if (ret != SUCCESS) { return ret; } } // write simulator config file if (bSimCfgFileName) { ret = writeSimCfg(distris, boundsVideo, strSimCfgFileName); if (ret != SUCCESS) { return ret; } } return SUCCESS; }