fp@1422: /***************************************************************************** fp@1422: * fp@1422: * $Id$ fp@1422: * fp@1422: * Copyright (C) 2006-2009 Florian Pose, Ingenieurgemeinschaft IgH fp@1422: * fp@1422: * This file is part of the IgH EtherCAT Master. fp@1422: * fp@1422: * The IgH EtherCAT Master is free software; you can redistribute it and/or fp@1422: * modify it under the terms of the GNU General Public License version 2, as fp@1422: * published by the Free Software Foundation. fp@1422: * fp@1422: * The IgH EtherCAT Master is distributed in the hope that it will be useful, fp@1422: * but WITHOUT ANY WARRANTY; without even the implied warranty of fp@1422: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General fp@1422: * Public License for more details. fp@1422: * fp@1422: * You should have received a copy of the GNU General Public License along fp@1422: * with the IgH EtherCAT Master; if not, write to the Free Software fp@1422: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA fp@1422: * fp@1422: * --- fp@1422: * fp@1422: * The license mentioned above concerns the source code only. Using the fp@1422: * EtherCAT technology and brand is only permitted in compliance with the fp@1422: * industrial property and similar rights of Beckhoff Automation GmbH. fp@1422: * fp@1422: ****************************************************************************/ fp@1422: fp@1422: #include fp@1422: #include fp@2673: #include fp@1422: using namespace std; fp@1422: fp@1422: #include "CommandGraph.h" fp@1826: #include "MasterDevice.h" fp@1422: fp@1422: /*****************************************************************************/ fp@1422: fp@1422: CommandGraph::CommandGraph(): fp@1422: Command("graph", "Output the bus topology as a graph.") fp@1422: { fp@1422: } fp@1422: fp@1422: /*****************************************************************************/ fp@1422: fp@1968: string CommandGraph::helpString(const string &binaryBaseName) const fp@1422: { fp@1422: stringstream str; fp@1422: fp@2673: str fp@2673: << binaryBaseName << " " << getName() << " [OPTIONS]" << endl fp@2673: << binaryBaseName << " " << getName() << " [OPTIONS] " << endl fp@1422: << endl fp@1422: << getBriefDescription() << endl fp@1422: << endl fp@1422: << "The bus is output in DOT language (see" << endl fp@1422: << "http://www.graphviz.org/doc/info/lang.html), which can" << endl fp@1422: << "be processed with the tools from the Graphviz" << endl fp@1422: << "package. Example:" << endl fp@1422: << endl fp@1422: << " ethercat graph | dot -Tsvg > bus.svg" << endl fp@1422: << endl fp@2673: << "See 'man dot' for more information." << endl fp@2673: << endl fp@2673: << "Additional information at edges and nodes is selected via" << endl fp@2673: << "the first argument:" << endl fp@2673: << " DC - DC timing" << endl fp@2673: << " CRC - CRC error register information" << endl fp@2673: << endl; fp@1422: fp@1422: return str.str(); fp@1422: } fp@1422: fp@1422: /****************************************************************************/ fp@1422: fp@2673: enum Info { fp@2673: None, fp@2673: DC, fp@2673: CRC fp@2673: }; fp@2673: fp@2673: #define REG_SIZE (20) fp@2673: fp@2673: struct CrcInfo { fp@2673: unsigned int crc[EC_MAX_PORTS]; fp@2673: unsigned int phy[EC_MAX_PORTS]; fp@2673: unsigned int fwd[EC_MAX_PORTS]; fp@2673: unsigned int lnk[EC_MAX_PORTS]; fp@2673: }; fp@2673: fp@1826: void CommandGraph::execute(const StringVector &args) fp@1422: { fp@2673: Info info = None; fp@1422: ec_ioctl_master_t master; fp@1422: typedef vector SlaveVector; fp@1422: SlaveVector slaves; fp@2673: typedef vector CrcInfoVector; fp@2673: CrcInfoVector crcInfos; fp@1422: ec_ioctl_slave_t slave; fp@1422: SlaveVector::const_iterator si; fp@1422: map portMedia; fp@1422: map::const_iterator mi; fp@1422: map mediaWeights; fp@1422: map::const_iterator wi; fp@1422: fp@1422: portMedia[EC_PORT_MII] = "MII"; fp@1422: mediaWeights[EC_PORT_MII] = 1; fp@1422: fp@1422: portMedia[EC_PORT_EBUS] = "EBUS"; fp@1422: mediaWeights[EC_PORT_EBUS] = 5; fp@2421: fp@2673: if (args.size() > 1) { fp@1422: stringstream err; fp@2673: err << "'" << getName() << "' takes either one or no arguments!"; fp@1422: throwInvalidUsageException(err); fp@1422: } fp@1422: fp@2673: if (args.size() == 1) { fp@2673: string arg = args[0]; fp@2673: transform(arg.begin(), arg.end(), fp@2673: arg.begin(), (int (*) (int)) std::toupper); fp@2673: if (arg == "DC") { fp@2673: info = DC; fp@2673: } fp@2673: else if (arg == "CRC") { fp@2673: info = CRC; fp@2673: } fp@2673: else { fp@2673: stringstream err; fp@2673: err << "Unknown argument \"" << args[0] << "\"!"; fp@2673: throwInvalidUsageException(err); fp@2673: } fp@2673: } fp@2673: fp@1870: MasterDevice m(getSingleMasterIndex()); fp@1422: m.open(MasterDevice::Read); fp@1422: m.getMaster(&master); fp@1422: fp@2673: for (unsigned int i = 0; i < master.slave_count; i++) { fp@1422: m.getSlave(&slave, i); fp@1422: slaves.push_back(slave); fp@2673: fp@2673: } fp@2673: fp@2673: if (info == CRC) { fp@2673: uint8_t data[REG_SIZE]; fp@2673: ec_ioctl_slave_reg_t io; fp@2673: io.emergency = 0; fp@2673: io.address = 0x0300; fp@2673: io.size = REG_SIZE; fp@2673: io.data = data; fp@2673: fp@2673: for (unsigned int i = 0; i < master.slave_count; i++) { fp@2673: io.slave_position = i; fp@2673: try { fp@2673: m.readReg(&io); fp@2673: } catch (MasterDeviceException &e) { fp@2673: throw e; fp@2673: } fp@2673: fp@2673: CrcInfo crcInfo; fp@2673: for (int port = 0; port < EC_MAX_PORTS; port++) { fp@2673: crcInfo.crc[port] = io.data[ 0 + port * 2]; fp@2673: crcInfo.phy[port] = io.data[ 1 + port * 2]; fp@2673: crcInfo.fwd[port] = io.data[ 8 + port]; fp@2673: crcInfo.lnk[port] = io.data[16 + port]; fp@2673: } fp@2673: crcInfos.push_back(crcInfo); fp@2673: } fp@1422: } fp@1422: fp@1422: cout << "/* EtherCAT bus graph. Generated by 'ethercat graph'. */" << endl fp@1422: << endl fp@1422: << "strict graph bus {" << endl fp@1422: << " rankdir=\"LR\"" << endl fp@1425: << " ranksep=0.8" << endl fp@1425: << " nodesep=0.8" << endl fp@1425: << " node [fontname=\"Helvetica\"]" << endl fp@1425: << " edge [fontname=\"Helvetica\",fontsize=\"10\"]" << endl fp@1422: << endl fp@1425: << " master [label=\"EtherCAT\\nMaster\"]" << endl; fp@1422: fp@1422: if (slaves.size()) { fp@2421: cout << " master -- slave0"; fp@1425: mi = portMedia.find(slaves.front().ports[0].desc); fp@1425: if (mi != portMedia.end()) fp@1425: cout << "[label=\"" << mi->second << "\"]"; fp@1422: fp@1425: cout << endl; fp@1422: } fp@1422: cout << endl; fp@1422: fp@2671: uint16_t alias = 0x0000; fp@2671: uint16_t pos = 0; fp@2671: fp@1422: for (si = slaves.begin(); si != slaves.end(); si++) { fp@2671: if (si->alias) { fp@2671: alias = si->alias; fp@2671: pos = 0; fp@2671: } fp@2671: fp@1804: cout << " slave" << si->position << " [shape=\"box\"" fp@2671: << ",label=\"" << si->position fp@2671: << " / " << alias << ":" << pos; fp@1422: if (string(si->order).size()) fp@1422: cout << "\\n" << si->order; fp@2673: if (info == DC && si->dc_supported) { fp@1425: cout << "\\nDC: "; fp@1425: if (si->has_dc_system_time) { fp@1425: switch (si->dc_range) { fp@1425: case EC_DC_32: fp@1425: cout << "32 bit"; fp@1425: break; fp@1425: case EC_DC_64: fp@1425: cout << "64 bit"; fp@1425: break; fp@1425: default: fp@1425: break; fp@1425: } fp@1425: } else { fp@1425: cout << "Delay meas."; fp@1425: } fp@1426: cout << "\\nDelay: " << si->transmission_delay << " ns"; fp@1425: } fp@1422: cout << "\"]" << endl; fp@1422: fp@2673: fp@2673: for (int port = 1; port < EC_MAX_PORTS; port++) { fp@2673: uint16_t next_pos = si->ports[port].next_slave; fp@2673: fp@2673: if (next_pos == 0xffff) { fp@1422: continue; fp@2673: } fp@2673: fp@2673: if (next_pos >= slaves.size()) { fp@1425: cerr << "Invalid next slave pointer." << endl; fp@2673: continue; fp@2673: } fp@2673: fp@2673: ec_ioctl_slave_t *next = &slaves[next_pos]; fp@1425: fp@1422: cout << " slave" << si->position << " -- " fp@2673: << "slave" << next_pos << " [taillabel=\"" << port; fp@2673: fp@2673: if (info == DC && si->dc_supported) { fp@2673: cout << " [" << si->ports[port].delay_to_next_dc << "]"; fp@2673: } fp@2673: if (info == CRC) { fp@2673: CrcInfo *crcInfo = &crcInfos[si->position]; fp@2673: cout << " [" << crcInfo->crc[port] << "/" fp@2673: << crcInfo->fwd[port] << "]"; fp@2673: } fp@2673: fp@1425: cout << "\",headlabel=\"0"; fp@1425: fp@2673: if (info == DC && next->dc_supported) { fp@1425: cout << " [" << next->ports[0].delay_to_next_dc << "]"; fp@1425: } fp@2673: if (info == CRC) { fp@2673: CrcInfo *crcInfo = &crcInfos[next_pos]; fp@2673: cout << " [" << crcInfo->crc[0] << "/" fp@2673: << crcInfo->fwd[0] << "]"; fp@2673: } fp@1425: cout << "\""; fp@1425: fp@2673: mi = portMedia.find(si->ports[port].desc); fp@1425: if (mi == portMedia.end() && next) { fp@1422: /* Try medium of next-hop slave. */ fp@1425: mi = portMedia.find(next->ports[0].desc); fp@1422: } fp@2421: fp@1422: if (mi != portMedia.end()) fp@1422: cout << ",label=\"" << mi->second << "\""; fp@1422: fp@2673: wi = mediaWeights.find(si->ports[port].desc); fp@1422: if (wi != mediaWeights.end()) fp@1422: cout << ",weight=\"" << wi->second << "\""; fp@2421: fp@1422: cout << "]" << endl; fp@1422: } fp@1422: fp@1422: cout << endl; fp@2671: pos++; fp@1422: } fp@1422: fp@1422: cout << "}" << endl; fp@1422: } fp@1422: fp@1422: /*****************************************************************************/