/* * Copyright information and license terms for this software can be * found in the file LICENSE that is included with the distribution */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pvutils.h" namespace { void usage (bool details=false) { fprintf (stderr, "Usage: pvput [options] \n" " pvput [options] [ ...]\n" " pvput [options] = ...\n" " pvput [options] \n" " pvput [options] \n" "\n" COMMON_OPTIONS " Deprecated options:\n" " default: Auto - try value as enum string, then as index number\n" " -n, -s, -F, -t: ignored\n" " -f : error" , request.c_str(), timeout, defaultProvider.c_str()); if(details) { fprintf (stderr, "\n JSON support is present\n" "\nExamples:\n" "\n" " pvput double01 1.234 # shorthand\n" " pvput double01 value=1.234\n" "\n" " pvput arr:pv X 1.0 2.0 # shorthand (X is arbitrary and ignored)\n" " pvput arr:pv \"[1.0, 2.0]\" # shorthand\n" " pvput arr:pv value=\"[1.0, 2.0]\"\n" "\n" "Field values may be given with JSON syntax.\n" "\n" "Complete structure\n" "\n" " pvput double01 '{\"value\":1.234}'\n" "\n" "Sub-structure(s)\n" "\n" " pvput group:pv some='{\"value\":1.234}' other='{\"value\":\"a string\"}'\n" "\n" ); } } void printValue(std::string const & channelName, pvd::PVStructure::const_shared_pointer const & pv) { std::cout<stream().format(outmode); std::cout.flush(); } struct Putter : public pvac::ClientChannel::PutCallback { epicsEvent wait; epicsMutex lock; bool done; pvac::PutEvent::event_t result; std::string message; Putter() :done(false) {} std::vector values; pvd::shared_vector jarr; virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args) { if(debugFlag) std::cerr<<"Server defined structure\n"<createPVStructure(build)); typedef pvd::shared_vector bare_t; bare_t bare; typedef std::pair KV_t; typedef std::vector pairs_t; pairs_t pairs; for(size_t i=0, N=values.size(); igetSubField(fname)); if(fld) { // The "field" exist. Treat this input as a filed=value pair. pairs.push_back(std::make_pair(fname, values[i].substr(sep+1))); } else { // If the "field" does not exist, this could be a bare value containing a "=" char. // The ".value" field must exist and be of type "string". Otherwise, the field was // incorrect and we ignore it. pvd::PVFieldPtr fldv(root->getSubFieldT("value")); if (fldv) { if ((pvd::scalar==fldv->getField()->getType()) && (0==fldv->getField()->getID().compare("string"))) // Tread it as a bare value bare.push_back(values[i]); else // Ignore it fprintf(stderr, "%s : Warning: no such field. Ignoring it.\n", fname.c_str()); } } } } if(!bare.empty() && !pairs.empty()) { throw std::runtime_error("Can't mix bare values and field=value pairs"); } else if(bare.empty() && pairs.empty()) { // We could have ignored all the value at this point throw std::runtime_error("No valid value(s) specified"); } else if(bare.size()==1 && bare[0][0]=='[') { // treat plain "[...]" as "value=[...]" pairs.push_back(std::make_pair("value", bare[0])); bare.clear(); } if(bare.size()==1 && bare[0][0]=='{') { if(debugFlag) fprintf(stderr, "In JSON top mode\n"); std::istringstream strm(bare[0]); parseJSON(strm, root, &args.tosend); } else if(pairs.empty()) { if(debugFlag) fprintf(stderr, "In plain value mode\n"); pvd::PVFieldPtr fld(root->getSubField("value")); if(!fld) throw std::runtime_error("Structure has no .value"); pvd::Type ftype = fld->getField()->getType(); if(ftype==pvd::scalar) { if(bare.size()!=1) { throw std::runtime_error("Can't assign multiple values to scalar"); } pvd::PVScalar* sfld(static_cast(fld.get())); sfld->putFrom(bare[0]); args.tosend.set(sfld->getFieldOffset()); } else if(ftype==pvd::scalarArray) { pvd::PVScalarArray* sfld(static_cast(fld.get())); // first element is "length" which we ignore for compatibility bare.slice(1); sfld->putFrom(freeze(bare)); args.tosend.set(sfld->getFieldOffset()); } else if(ftype==pvd::structure && fld->getField()->getID()=="enum_t") { if(bare.size()!=1) { throw std::runtime_error("Can't assign multiple values to enum"); } pvd::PVStructure* sfld(static_cast(fld.get())); assert(!!args.previous); // ensure by calling put(..., true) below pvd::PVScalar* idxfld(sfld->getSubFieldT("index").get()); pvd::PVStringArray::const_svector choices(args.previous->getSubFieldT("value.choices")->view()); bool found=false; for(size_t i=0; iputFrom(i); found=true; break; } } if(!found) { // try to parse as integer idxfld->putFrom(bare[0]); } args.tosend.set(idxfld->getFieldOffset()); } else { throw std::runtime_error("Don't know how to set field .value"); } } else { if(debugFlag) fprintf(stderr, "In field=value mode\n"); for(pairs_t::const_iterator it=pairs.begin(), end=pairs.end(); it!=end; ++it) { pvd::PVFieldPtr fld(root->getSubField(it->first)); if(!fld) { fprintf(stderr, "%s : Warning: no such field\n", it->first.c_str()); // ignore } else if(it->second[0]=='[') { pvd::shared_vector arr; jarray(arr, it->second.c_str()); pvd::PVScalarArray* afld(dynamic_cast(fld.get())); if(!afld) { fprintf(stderr, "%s : Error not a scalar array field\n", it->first.c_str()); throw std::runtime_error("Not a scalar array field"); } afld->putFrom(freeze(arr)); args.tosend.set(afld->getFieldOffset()); } else if(it->second[0]=='{' || it->second[0]=='[') { std::istringstream strm(it->second); parseJSON(strm, fld, &args.tosend); } else { pvd::PVScalarPtr sfld(std::tr1::dynamic_pointer_cast(fld)); if(!sfld) { fprintf(stderr, "%s : Error: need a scalar field\n", it->first.c_str()); } else { sfld->putFrom(it->second); args.tosend.set(sfld->getFieldOffset()); } } } } args.root = root; if(debugFlag) std::cout<<"To be sent: "< G(lock); result = evt.event; message = evt.message; done = true; } wait.signal(); } }; } // namespace int main (int argc, char *argv[]) { try { int opt; /* getopt() current option */ bool quiet = false; setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */ putenv(const_cast("POSIXLY_CORRECT=")); /* Behave correct on GNU getopt systems; e.g. handle negative numbers */ while ((opt = getopt(argc, argv, ":hvVM:r:w:tp:qdF:f:ns")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(true); return 0; case 'v': outmode = pvd::PVStructure::Formatter::Raw; break; case 'V': /* Print version */ { fprintf(stdout, "pvAccess %u.%u.%u%s\n", EPICS_PVA_MAJOR_VERSION, EPICS_PVA_MINOR_VERSION, EPICS_PVA_MAINTENANCE_VERSION, (EPICS_PVA_DEVELOPMENT_FLAG)?"-SNAPSHOT":""); fprintf(stdout, "pvData %u.%u.%u%s\n", EPICS_PVD_MAJOR_VERSION, EPICS_PVD_MINOR_VERSION, EPICS_PVD_MAINTENANCE_VERSION, (EPICS_PVD_DEVELOPMENT_FLAG)?"-SNAPSHOT":""); fprintf(stdout, "Base %s\n", EPICS_VERSION_FULL); return 0; } case 'M': if(strcmp(optarg, "raw")==0) { outmode = pvd::PVStructure::Formatter::Raw; } else if(strcmp(optarg, "nt")==0) { outmode = pvd::PVStructure::Formatter::NT; } else if(strcmp(optarg, "json")==0) { outmode = pvd::PVStructure::Formatter::JSON; } else { fprintf(stderr, "Unknown output mode '%s'\n", optarg); outmode = pvd::PVStructure::Formatter::Raw; } break; case 'w': /* Set PVA timeout value */ { double temp; if((epicsScanDouble(optarg, &temp)) != 1) { fprintf(stderr, "'%s' is not a valid timeout value " "- ignored. ('pvput -h' for help.)\n", optarg); } else { timeout = temp; } } break; case 'r': /* Set PVA timeout value */ request = optarg; break; case 't': /* Terse mode */ // deprecated break; case 'd': /* Debug log level */ debugFlag = true; break; case 'p': /* Set default provider */ defaultProvider = optarg; break; case 'q': /* Quiet mode */ quiet = true; break; case 'F': /* Store this for output formatting */ break; case 'f': /* Use input stream as input */ fprintf(stderr, "Unsupported option -f\n"); return 1; case 'n': break; case 's': break; case '?': fprintf(stderr, "Unrecognized option: '-%c'. ('pvput -h' for help.)\n", optopt); return 1; case ':': fprintf(stderr, "Option '-%c' requires an argument. ('pvput -h' for help.)\n", optopt); return 1; default : usage(); return 1; } } if (argc <= optind) { fprintf(stderr, "No pv name specified. ('pvput -h' for help.)\n"); return 1; } std::string pvName(argv[optind++]); std::string providerName(defaultProvider); int nVals = argc - optind; /* Remaining arg list are PV names */ if (nVals < 1) { fprintf(stderr, "No value(s) specified. ('pvput -h' for help.)\n"); return 1; } Putter thework; // copy values from command line for (int n = 0; optind < argc; n++, optind++) thework.values.push_back(argv[optind]); pvd::PVStructure::shared_pointer pvRequest; try { pvRequest = pvd::createRequest(request); } catch(std::exception& e){ fprintf(stderr, "failed to parse request string: %s\n", e.what()); return 1; } SET_LOG_LEVEL(debugFlag ? pva::logLevelDebug : pva::logLevelError); std::cout << std::boolalpha; epics::pvAccess::ca::CAClientFactory::start(); pvac::ClientProvider ctxt(providerName); pvac::ClientChannel chan(ctxt.connect(pvName)); if (!quiet) { std::cout << "Old : "; printValue(pvName, chan.get(timeout, pvRequest)); } { pvac::Operation op(chan.put(&thework, pvRequest, true)); epicsGuard G(thework.lock); while(!thework.done) { epicsGuardRelease U(G); if(!thework.wait.wait(timeout)) { fprintf(stderr, "Put timeout\n"); return 1; } } } if(thework.result==pvac::PutEvent::Fail) { fprintf(stderr, "Error: %s\n", thework.message.c_str()); } if (!quiet) { std::cout << "New : "; } printValue(pvName, chan.get(timeout, pvRequest)); return thework.result!=pvac::PutEvent::Success; } catch(std::exception& e) { std::cerr<<"Error: "<