14 #include <XPLMDisplay.h> 
   17 #include <acfutils/avl.h> 
   18 #include <acfutils/crc64.h> 
   19 #include <acfutils/math.h> 
   20 #include <acfutils/list.h> 
   21 #include <acfutils/perf.h> 
   22 #include <acfutils/safe_alloc.h> 
   23 #include <acfutils/worker.h> 
   25 #ifdef  LIBELEC_WITH_DRS 
   26 #include <acfutils/dr.h> 
   29 #ifdef  LIBELEC_WITH_LIBSWITCH 
   30 #include <libswitch.h> 
   33 #ifdef  LIBELEC_WITH_NETLINK 
   38 #include "libelec_types_impl.h" 
   40 #define EXEC_INTVAL     40000    
   41 #define MAX_NETWORK_DEPTH   100  
   42 #define NO_NEG_ZERO(x)      ((x) == 0.0 ? 0.0 : (x)) 
   43 #define CB_SW_ON_DELAY      0.33     
   44 #define MAX_COMPS       (UINT16_MAX + 1) 
   45 #define GEN_MIN_RPM     1e-3 
   47 #ifdef  LIBELEC_WITH_NETLINK 
   49 #define LIBELEC_NET_VERSION 1 
   50 #define NETMAPGET(map, idx) \ 
   51     ((((map)[(idx) >> 3]) & (1 << ((idx) & 7))) != 0) 
   52 #define NETMAPSET(map, idx) \ 
   54         (map)[(idx) >> 3] |= (1 << ((idx) & 7)); \ 
   56 #define NETMAPSZ(sys)       (list_count(&sys->comps) % 8 == 0 ? \ 
   57     (list_count(&sys->comps) / 8) : (list_count(&sys->comps) / 8 + 1)) 
   58 #define NETMAPSZ_REQ(sys)   (sizeof (net_req_map_t) + NETMAPSZ(sys)) 
   59 static bool send_net_recv_map(elec_sys_t *sys);
 
   60 static void net_add_recv_comp(elec_comp_t *comp);
 
   62 #define NET_XMIT_INTVAL     5    
   63 #define NET_VOLTS_FACTOR    20.0     
   64 #define NET_AMPS_FACTOR     40.0     
   65 #define NET_FREQ_FACTOR     20.0     
   67 #define NET_ADD_RECV_COMP(comp) net_add_recv_comp((elec_comp_t *)comp) 
   71 #ifdef  LIBELEC_NET_DBG 
   72 #define NET_DBG_LOG(...)    logMsg(__VA_ARGS__) 
   74 #define NET_DBG_LOG(...) 
   79 #define NET_ADD_RECV_COMP(comp) 
   94 static const vect2_t batt_temp_energy_curve[] = {
 
   95     {C2KELVIN(-90), 0.01},
 
   96     {C2KELVIN(-75), 0.01},
 
   97     {C2KELVIN(-50), 0.125},
 
   98     {C2KELVIN(-20), 0.45},
 
  100     {C2KELVIN(15), 0.925},
 
  105 static elec_comp_info_t *infos_parse(
const char *filename, 
size_t *num_infos);
 
  108 static bool_t elec_sys_worker(
void *userinfo);
 
  109 static void comp_free(elec_comp_t *comp);
 
  110 static void network_paint_src_comp(elec_comp_t *src, elec_comp_t *upstream,
 
  111     elec_comp_t *comp, 
unsigned depth);
 
  112 static double network_load_integrate_comp(
const elec_comp_t *src,
 
  113     const elec_comp_t *upstream, elec_comp_t *comp, 
unsigned depth, 
double d_t);
 
  114 static double network_load_integrate_load(
const elec_comp_t *src,
 
  115     elec_comp_t *comp, 
unsigned depth, 
double d_t);
 
  117 static double network_trace(
const elec_comp_t *upstream,
 
  118     const elec_comp_t *comp, 
unsigned depth, 
bool do_print);
 
  120 #ifdef  LIBELEC_WITH_NETLINK 
  122 static void netlink_send_msg_notif(netlink_conn_id_t conn_id, 
const void *buf,
 
  123     size_t bufsz, 
void *userinfo);
 
  124 static void netlink_recv_msg_notif(netlink_conn_id_t conn_id, 
const void *buf,
 
  125     size_t bufsz, 
void *userinfo);
 
  127 static void elec_net_send_update(elec_sys_t *sys);
 
  128 static void elec_net_recv_update(elec_sys_t *sys);
 
  140 elec_draw_cb(XPLMDrawingPhase phase, 
int before, 
void *refcon)
 
  143     double sim_time, time_factor;
 
  147     ASSERT(refcon != NULL);
 
  150     sim_time = dr_getf_prot(&sys->drs.sim_time);
 
  152     time_factor = round(dr_getf(&sys->drs.sim_speed_act) * 10) / 10;
 
  153     if (sys->prev_sim_time >= sim_time || dr_geti(&sys->drs.replay) != 0 ||
 
  154         dr_geti(&sys->drs.paused) != 0 || time_factor == 0) {
 
  157             worker_set_interval_nowake(&sys->worker, EXEC_INTVAL);
 
  158         mutex_enter(&sys->paused_lock);
 
  160         sys->time_factor = 0;
 
  161         mutex_exit(&sys->paused_lock);
 
  164     sys->prev_sim_time = sim_time;
 
  170     if ((time_factor != sys->time_factor ||
 
  171         (time_factor == 1 && sys->time_factor != 1)) && sys->started) {
 
  172         worker_set_interval_nowake(&sys->worker,
 
  173             EXEC_INTVAL / time_factor);
 
  175     mutex_enter(&sys->paused_lock);
 
  177     sys->time_factor = time_factor;
 
  178     mutex_exit(&sys->paused_lock);
 
  187     ASSERT(info != NULL);
 
  188     switch (info->type) {
 
  190         return (info->gen.freq != 0);
 
  201 check_upstream(
const elec_comp_t *comp, 
const elec_comp_t *src,
 
  202     const elec_comp_t *upstream)
 
  204     ASSERT(comp != NULL);
 
  206     ASSERT3U(src->src_idx, <, ELEC_MAX_SRCS);
 
  207     ASSERT(upstream != NULL);
 
  209     for (
unsigned i = 0; i < comp->n_links; i++) {
 
  210         if (comp->links[i].comp == upstream)
 
  211             return (comp->links[i].srcs[src->src_idx] == src);
 
  217 get_src_fract(
const elec_comp_t *comp, 
const elec_comp_t *src)
 
  219     ASSERT(comp != NULL);
 
  222     if (comp->src_int_cond_total > 1e-12) {
 
  223         double src_cond = (1.0 / src->info->int_R) * src->rw.out_volts;
 
  224         return (MIN(src_cond / comp->src_int_cond_total, 1));
 
  235     ASSERT(link != NULL);
 
  236     for (
unsigned i = 0; i < ELEC_MAX_SRCS; i++)
 
  237         amps += link->out_amps[i];
 
  243 user_cb_info_compar(
const void *a, 
const void *b)
 
  246     const uintptr_t cb_a = (uintptr_t)ucbi_a->cb;
 
  247     const uintptr_t cb_b = (uintptr_t)ucbi_a->cb;
 
  249     if (!ucbi_a->pre && ucbi_b->pre)
 
  251     if (ucbi_a->pre && !ucbi_b->pre)
 
  257     if (ucbi_a->userinfo < ucbi_b->userinfo)
 
  259     if (ucbi_a->userinfo > ucbi_b->userinfo)
 
  265 name2comp_compar(
const void *a, 
const void *b)
 
  267     const elec_comp_t *ca = a, *cb = b;
 
  268     int res = strcmp(ca->info->name, cb->info->name);
 
  277 info2comp_compar(
const void *a, 
const void *b)
 
  279     const elec_comp_t *ca = a, *cb = b;
 
  280     if (ca->info < cb->info)
 
  282     if (ca->info > cb->info)
 
  288 find_comp(elec_sys_t *sys, 
const elec_comp_info_t *info, 
const elec_comp_t *src)
 
  294     ASSERT(info != NULL);
 
  297     comp = avl_find(&sys->info2comp, &srch, NULL);
 
  298     ASSERT_MSG(comp != NULL, 
"Component for info %s not found " 
  299         "(referenced from %s)", srch.info->name, src->info->name);
 
  305 resolve_bus_links(elec_sys_t *sys, elec_comp_t *bus)
 
  309     ASSERT(bus->info != NULL);
 
  310     ASSERT3U(bus->info->type, ==, 
ELEC_BUS);
 
  312     bus->n_links = bus->info->bus.n_comps;
 
  313     bus->links = safe_calloc(bus->n_links, sizeof (*bus->links));
 
  315 #define CHECK_COMP(cond, reason) \ 
  318             logMsg("%s (%s:%d): %s", comp->info->name, \
 
  319                 sys->conf_filename, comp->info->parse_linenum, \
 
  324 #define CHECK_COMP_V(cond, reason, ...) \ 
  327             logMsg("%s (%s:%d): " reason, comp->info->name, \
 
  328                 sys->conf_filename, comp->info->parse_linenum, \
 
  334     for (
size_t i = 0; i < bus->info->bus.n_comps; i++) {
 
  335         elec_comp_t *comp = find_comp(sys, bus->info->bus.comps[i],
 
  338         bus->links[i].comp = comp;
 
  339         ASSERT(comp->info != NULL);
 
  340         switch (comp->info->type) {
 
  343             CHECK_COMP(!bus->info->bus.ac, 
"batteries cannot " 
  344                 "connect to AC buses (batteries are inherently " 
  346             ASSERT3U(comp->n_links, ==, 1);
 
  347             ASSERT(comp->links != NULL);
 
  348             comp->links[0].comp = bus;
 
  352             CHECK_COMP_V(bus->info->bus.ac == src_is_AC(comp->info),
 
  353                 "AC/DC status is mismatched between the generator " 
  354                 "and its output bus %s", bus->info->name);
 
  355             ASSERT3U(comp->n_links, ==, 1);
 
  356             ASSERT(comp->links != NULL);
 
  357             comp->links[0].comp = bus;
 
  360             ASSERT3U(comp->n_links, ==, 2);
 
  361             ASSERT(comp->links != NULL);
 
  362             if (comp->info->tru.ac == bus->info) {
 
  363                 CHECK_COMP_V(bus->info->bus.ac, 
"input to the " 
  364                     "TRU must connect to an AC bus, but " 
  365                     "%s is DC", bus->info->name);
 
  366                 comp->links[0].comp = bus;
 
  368                 ASSERT3P(comp->info->tru.dc, ==, bus->info);
 
  369                 CHECK_COMP_V(!bus->info->bus.ac, 
"output of " 
  370                     "the TRU must connect to a DC bus, " 
  371                     "but %s is AC", bus->info->name);
 
  372                 comp->links[1].comp = bus;
 
  376             ASSERT3U(comp->n_links, ==, 2);
 
  377             ASSERT(comp->links != NULL);
 
  378             if (comp->info->tru.dc == bus->info) {
 
  379                 CHECK_COMP_V(!bus->info->bus.ac, 
"input to " 
  380                     "the inverter must connect to a DC bus, " 
  381                     "but %s is AC", bus->info->name);
 
  382                 comp->links[0].comp = bus;
 
  384                 ASSERT3P(comp->info->tru.ac, ==, bus->info);
 
  385                 CHECK_COMP_V(bus->info->bus.ac, 
"output of " 
  386                     "the inverter must connect to an AC bus, " 
  387                     "but %s is DC", bus->info->name);
 
  388                 comp->links[1].comp = bus;
 
  392             ASSERT3U(comp->n_links, ==, 2);
 
  393             ASSERT(comp->links != NULL);
 
  394             if (comp->info->xfrmr.input == bus->info) {
 
  395                 CHECK_COMP_V(bus->info->bus.ac, 
"input to " 
  396                     "the transformer must connect to an AC " 
  397                     "bus, but %s is DC", bus->info->name);
 
  398                 comp->links[0].comp = bus;
 
  400                 ASSERT3P(comp->info->xfrmr.output, ==,
 
  402                 CHECK_COMP_V(bus->info->bus.ac, 
"output of " 
  403                     "the transformer must connect to an " 
  404                     "AC bus, but %s is DC", bus->info->name);
 
  405                 comp->links[1].comp = bus;
 
  409             CHECK_COMP_V(bus->info->bus.ac == comp->info->load.ac,
 
  410                 "cannot connect %s load to %s bus",
 
  411                 comp->info->load.ac ? 
"AC" : 
"DC",
 
  412                 bus->info->bus.ac ? 
"AC" : 
"DC");
 
  413             ASSERT3U(comp->n_links, ==, 1);
 
  414             ASSERT(comp->links != NULL);
 
  415             comp->links[0].comp = bus;
 
  418             CHECK_COMP_V(
false, 
"Invalid link: cannot connect " 
  419                 "bus %s directly to bus %s", bus->info->name,
 
  425             if (comp->info->type == 
ELEC_CB) {
 
  426                 CHECK_COMP(!comp->info->cb.triphase ||
 
  427                     bus->info->bus.ac, 
"3-phase breakers " 
  428                     "cannot be connected to DC buses");
 
  430             ASSERT3U(comp->n_links, ==, 2);
 
  431             ASSERT(comp->links != NULL);
 
  432             if (comp->links[0].comp == NULL) {
 
  433                 comp->links[0].comp = bus;
 
  435                 elec_comp_t *other_bus = comp->links[0].comp;
 
  437                 CHECK_COMP(comp->links[1].comp == NULL,
 
  438                     "too many connections");
 
  439                 comp->links[1].comp = bus;
 
  440                 CHECK_COMP_V(bus->info->bus.ac ==
 
  441                     other_bus->info->bus.ac, 
"cannot link " 
  442                     "two buses of incompatible type (%s is " 
  445                     bus->info->bus.ac ? 
"AC" : 
"DC",
 
  446                     other_bus->info->name,
 
  447                     other_bus->info->bus.ac ? 
"AC" : 
"DC");
 
  452             comp->links = safe_realloc(comp->links,
 
  453                 comp->n_links * sizeof (*comp->links));
 
  454             comp->links[comp->n_links - 1].comp = bus;
 
  455             free(comp->tie.cur_state);
 
  456             comp->tie.cur_state = safe_calloc(comp->n_links,
 
  457                 sizeof (*comp->tie.cur_state));
 
  458             free(comp->tie.wk_state);
 
  459             comp->tie.wk_state = safe_calloc(comp->n_links,
 
  460                 sizeof (*comp->tie.wk_state));
 
  467             CHECK_COMP_V(!bus->info->bus.ac, 
"cannot connect " 
  468                 "diode %s to an AC bus (libelec cannot be used " 
  469                 "to build a bridge rectifier, use a \"TRU\" " 
  470                 "component for that)", comp->info->name);
 
  471             ASSERT3U(comp->n_links, ==, 2);
 
  472             ASSERT(comp->links != NULL);
 
  473             if (comp->info->diode.sides[0] == bus->info) {
 
  474                 comp->links[0].comp = bus;
 
  476                 ASSERT3P(comp->info->diode.sides[1], ==,
 
  478                 comp->links[1].comp = bus;
 
  491 resolve_comp_links(elec_sys_t *sys)
 
  495     for (elec_comp_t *comp = list_head(&sys->comps); comp != NULL;
 
  496         comp = list_next(&sys->comps, comp)) {
 
  498             if (!resolve_bus_links(sys, comp))
 
  500         } 
else if (comp->info->type == 
ELEC_TRU &&
 
  501             comp->info->tru.charger) {
 
  502             comp->tru.batt = find_comp(sys,
 
  503                 comp->info->tru.batt, comp);
 
  504             comp->tru.batt_conn = find_comp(sys,
 
  505                 comp->info->tru.batt_conn, comp);
 
  511 WARN_UNUSED_RES_ATTR 
static bool 
  512 check_comp_links(elec_sys_t *sys)
 
  516     for (elec_comp_t *comp = list_head(&sys->comps); comp != NULL;
 
  517         comp = list_next(&sys->comps, comp)) {
 
  518         ASSERT(comp->info != NULL);
 
  519         for (
unsigned i = 0; i < comp->n_links; i++) {
 
  520             if (comp->links[i].comp == NULL) {
 
  521                 logMsg(
"Component %s is missing a network link",
 
  542     ASSERT(num_infos != NULL);
 
  543     *num_infos = sys->num_infos;
 
  544     return (sys->comp_infos);
 
  550     elec_comp_t *comp = safe_calloc(1, 
sizeof (*comp));
 
  552     ASSERT(info != NULL);
 
  556     mutex_init(&comp->rw_ro_lock);
 
  557     VERIFY_MSG(avl_find(&sys->info2comp, comp, &where) == NULL,
 
  558         "Duplicate elec info usage %s", info->name);
 
  559     avl_insert(&sys->info2comp, comp, where);
 
  560     list_insert_tail(&sys->comps, comp);
 
  562     VERIFY_MSG(avl_find(&sys->name2comp, comp, &where) ==
 
  563         NULL, 
"Duplicate info name %s", info->name);
 
  564     avl_insert(&sys->name2comp, comp, where);
 
  568     switch (comp->info->type) {
 
  570         comp->load.random_load_factor = 1;
 
  573         comp->src_idx = *src_i;
 
  575         mutex_init(&comp->batt.lock);
 
  576         comp->batt.chg_rel = 1.0;
 
  577         comp->batt.T = C2KELVIN(15);
 
  580         comp->src_idx = *src_i;
 
  582         mutex_init(&comp->gen.lock);
 
  583         comp->gen.tgt_volts = comp->info->gen.volts;
 
  584         comp->gen.tgt_freq = comp->info->gen.freq;
 
  585         comp->gen.ctr_rpm = AVG(comp->info->gen.min_rpm,
 
  586             comp->info->gen.max_rpm);
 
  587         comp->gen.rpm = GEN_MIN_RPM;
 
  588         comp->gen.max_stab_U =
 
  589             comp->gen.ctr_rpm / comp->info->gen.min_rpm;
 
  590         comp->gen.min_stab_U =
 
  591             comp->gen.ctr_rpm / comp->info->gen.max_rpm;
 
  592         if (comp->info->gen.stab_rate_f > 0) {
 
  593             comp->gen.max_stab_f = comp->gen.ctr_rpm /
 
  594                 comp->info->gen.min_rpm;
 
  595             comp->gen.min_stab_f = comp->gen.ctr_rpm /
 
  596                 comp->info->gen.max_rpm;
 
  602         comp->src_idx = *src_i;
 
  606         comp->src_idx = *src_i;
 
  614         comp->scb.cur_set = comp->scb.wk_set = 
true;
 
  617         mutex_init(&comp->tie.lock);
 
  627     switch (comp->info->type) {
 
  646     if (comp->n_links != 0)
 
  647         comp->links = safe_calloc(comp->n_links, sizeof (*comp->links));
 
  651     if (comp->src_idx > ELEC_MAX_SRCS) {
 
  652         logMsg(
"%s:%d: too many electrical sources (max: 64).",
 
  653             sys->conf_filename, info->parse_linenum);
 
  661         list_insert_tail(&sys->gens_batts, comp);
 
  662     else if (comp->info->type == 
ELEC_TIE)
 
  663         list_insert_tail(&sys->ties, comp);
 
  667 #ifdef  LIBELEC_WITH_DRS 
  668     dr_create_f64(&comp->drs.in_volts, &comp->ro.in_volts,
 
  669         false, 
"libelec/comp/%s/in_volts", comp->info->name);
 
  670     dr_create_f64(&comp->drs.out_volts, &comp->ro.out_volts,
 
  671         false, 
"libelec/comp/%s/out_volts", comp->info->name);
 
  672     dr_create_f64(&comp->drs.in_amps, &comp->ro.in_amps,
 
  673         false, 
"libelec/comp/%s/in_amps", comp->info->name);
 
  674     dr_create_f64(&comp->drs.out_amps, &comp->ro.out_amps,
 
  675         false, 
"libelec/comp/%s/out_amps", comp->info->name);
 
  676     dr_create_f64(&comp->drs.in_pwr, &comp->ro.in_pwr,
 
  677         false, 
"libelec/comp/%s/in_pwr", comp->info->name);
 
  678     dr_create_f64(&comp->drs.out_pwr, &comp->ro.out_pwr,
 
  679         false, 
"libelec/comp/%s/out_pwr", comp->info->name);
 
  739     elec_sys_t *sys = safe_calloc(1, 
sizeof (*sys));
 
  740     unsigned src_i = 0, comp_i = 0;
 
  744     ASSERT(filename != NULL);
 
  746     buf = file2buf(filename, &bufsz);
 
  748         logMsg(
"Can't open %s: %s", filename, strerror(errno));
 
  752     sys->conf_filename = safe_strdup(filename);
 
  753     sys->conf_crc = crc64(buf, bufsz);
 
  756     sys->comp_infos = infos_parse(filename, &sys->num_infos);
 
  757     if (sys->comp_infos == NULL) {
 
  761     list_create(&sys->comps, sizeof (elec_comp_t),
 
  762         offsetof(elec_comp_t, comps_node));
 
  763     list_create(&sys->gens_batts, sizeof (elec_comp_t),
 
  764         offsetof(elec_comp_t, gens_batts_node));
 
  765     list_create(&sys->ties, sizeof (elec_comp_t),
 
  766         offsetof(elec_comp_t, ties_node));
 
  767     avl_create(&sys->info2comp, info2comp_compar, sizeof (elec_comp_t),
 
  768         offsetof(elec_comp_t, info2comp_node));
 
  769     avl_create(&sys->name2comp, name2comp_compar, sizeof (elec_comp_t),
 
  770         offsetof(elec_comp_t, name2comp_node));
 
  772     mutex_init(&sys->user_cbs_lock);
 
  773     avl_create(&sys->user_cbs, user_cb_info_compar,
 
  775     mutex_init(&sys->worker_interlock);
 
  776     mutex_init(&sys->paused_lock);
 
  777     sys->time_factor = 1;
 
  779     for (
size_t i = 0; i < sys->num_infos; i++) {
 
  780         if (!comp_alloc(sys, &sys->comp_infos[i], &src_i))
 
  784     if (!resolve_comp_links(sys) || !check_comp_links(sys))
 
  789     ASSERT3U(list_count(&sys->comps), <=, MAX_COMPS);
 
  790     sys->comps_array = safe_calloc(list_count(&sys->comps),
 
  791         sizeof (*sys->comps_array));
 
  792     for (elec_comp_t *comp = list_head(&sys->comps); comp != NULL;
 
  793         comp = list_next(&sys->comps, comp)) {
 
  794         sys->comps_array[comp_i] = comp;
 
  795         comp->comp_idx = comp_i;
 
  799     fdr_find(&sys->drs.sim_speed_act, 
"sim/time/sim_speed_actual");
 
  800     fdr_find(&sys->drs.sim_time, 
"sim/time/total_running_time_sec");
 
  801     fdr_find(&sys->drs.paused, 
"sim/time/paused");
 
  802     fdr_find(&sys->drs.replay, 
"sim/time/is_in_replay");
 
  803     VERIFY(XPLMRegisterDrawCallback(elec_draw_cb, xplm_Phase_Window,
 
  822 validate_elec_comp_infos_parse(
const elec_comp_info_t *infos, 
size_t n_infos,
 
  823     const char *filename)
 
  825     ASSERT(infos != NULL || n_infos == 0);
 
  826     ASSERT(filename != NULL);
 
  828 #define CHECK_COMP(cond, reason) \ 
  831             logMsg("%s (%s:%d): %s", info->name, filename, \
 
  832                 info->parse_linenum, (reason)); \
 
  837     for (
size_t i = 0; i < n_infos; i++) {
 
  840         switch (info->type) {
 
  842             CHECK_COMP(info->batt.volts > 0,
 
  843                 "missing required \"VOLTS\" parameter");
 
  844             CHECK_COMP(info->batt.max_pwr > 0,
 
  845                 "missing required \"MAX_PWR\" parameter");
 
  846             CHECK_COMP(info->batt.capacity >= 0,
 
  847                 "missing required \"CAPACITY\" parameter");
 
  848             CHECK_COMP(info->batt.chg_R > 0,
 
  849                 "missing required \"CHG_R\" parameter");
 
  852             CHECK_COMP(info->gen.volts > 0,
 
  853                 "missing required \"VOLTS\" parameter");
 
  854             CHECK_COMP(info->gen.exc_rpm <= info->gen.min_rpm,
 
  855                 "\"EXC_RPM\" parameter must be less than or " 
  856                 "equal to \"MIN_RPM\"");
 
  857             CHECK_COMP(info->gen.min_rpm > 0,
 
  858                 "missing required \"MIN_RPM\" parameter");
 
  859             CHECK_COMP(info->gen.max_rpm > 0,
 
  860                 "missing required \"MAX_RPM\" parameter");
 
  861             CHECK_COMP(info->gen.min_rpm < info->gen.max_rpm,
 
  862                 "\"MIN_RPM\" must be lower than \"MAX_RPM\"");
 
  863             CHECK_COMP(info->gen.eff_curve != NULL, 
"generators " 
  864                 "require at least two \"CURVEPT EFF\" parameters");
 
  868             CHECK_COMP(info->tru.in_volts > 0,
 
  869                 "missing required \"IN_VOLTS\" parameter");
 
  870             CHECK_COMP(info->tru.out_volts > 0,
 
  871                 "missing required \"OUT_VOLTS\" parameter");
 
  873                 CHECK_COMP(info->tru.out_freq > 0,
 
  874                     "missing required \"OUT_FREQ\" parameter");
 
  876             CHECK_COMP(info->tru.eff_curve != NULL,
 
  877                 "at least two \"CURVEPT EFF\" parameters required");
 
  878             CHECK_COMP(info->tru.ac != NULL,
 
  879                 "AC side not connected");
 
  880             CHECK_COMP(info->tru.dc != NULL,
 
  881                 "DC side not connected");
 
  884             CHECK_COMP(info->xfrmr.in_volts > 0,
 
  885                 "missing required \"IN_VOLTS\" parameter");
 
  886             CHECK_COMP(info->xfrmr.out_volts > 0,
 
  887                 "missing required \"OUT_VOLTS\" parameter");
 
  888             CHECK_COMP(info->xfrmr.input != NULL,
 
  889                 "input side not connected");
 
  890             CHECK_COMP(info->xfrmr.output != NULL,
 
  891                 "output side not connected");
 
  898             CHECK_COMP(info->load.min_volts > 0 ||
 
  899                 !info->load.stab, 
"loads must specify " 
  900                 "a \"MIN_VOLTS\" when \"STAB\" is set to TRUE");
 
  901             ASSERT3F(info->load.incap_C, >=, 0);
 
  902             if (info->load.incap_C > 0)
 
  903                 ASSERT3F(info->load.incap_R, >, 0);
 
  906             CHECK_COMP(info->bus.comps != NULL,
 
  907                 "buses must connect to at least 1 component");
 
  914             CHECK_COMP(info->diode.sides[0] != NULL &&
 
  915                 info->diode.sides[1] != NULL,
 
  916                 "diodes need to have both end points connected");
 
  939     return (sys->started);
 
  954     return (!sys->started);
 
  984 #ifndef LIBELEC_SLOW_DEBUG 
  985         worker_init(&sys->worker, elec_sys_worker, EXEC_INTVAL, sys,
 
  988         worker_init(&sys->worker, elec_sys_worker, 0, sys, 
"elec_sys");
 
 1005     ASSERT(sys != NULL);
 
 1010     worker_fini(&sys->worker);
 
 1011 #ifdef  LIBELEC_WITH_NETLINK 
 1012     if (sys->net_recv.active) {
 
 1013         memset(sys->net_recv.map, 0, NETMAPSZ(sys));
 
 1014         send_net_recv_map(sys);
 
 1017     sys->started = 
false;
 
 1046     ASSERT(sys != NULL);
 
 1047     ASSERT3F(time_factor, >=, 0);
 
 1049     if (time_factor == 0) {
 
 1052             worker_set_interval_nowake(&sys->worker, EXEC_INTVAL);
 
 1053         mutex_enter(&sys->paused_lock);
 
 1055         sys->time_factor = 0;
 
 1056         mutex_exit(&sys->paused_lock);
 
 1064     if ((fabs(time_factor - sys->time_factor) > 0.1 ||
 
 1065         (time_factor == 1 && sys->time_factor != 1)) && sys->started) {
 
 1066         worker_set_interval_nowake(&sys->worker,
 
 1067             EXEC_INTVAL / time_factor);
 
 1069     mutex_enter(&sys->paused_lock);
 
 1070     sys->paused = 
false;
 
 1071     sys->time_factor = time_factor;
 
 1072     mutex_exit(&sys->paused_lock);
 
 1083     ASSERT(sys != NULL);
 
 1084     return (sys->time_factor);
 
 1088 elec_comp_serialize(elec_comp_t *comp, conf_t *ser, 
const char *prefix)
 
 1090     ASSERT(comp != NULL);
 
 1091     ASSERT(comp->info != NULL);
 
 1092     ASSERT(comp->info->name != NULL);
 
 1093     ASSERT(ser != NULL);
 
 1094     ASSERT(prefix != NULL);
 
 1096     LIBELEC_SERIALIZE_DATA_V(comp, ser, 
"%s/%s/data",
 
 1097         prefix, comp->info->name);
 
 1099     switch (comp->info->type) {
 
 1101         LIBELEC_SERIALIZE_DATA_V(&comp->batt, ser, 
"%s/%s/batt",
 
 1102             prefix, comp->info->name);
 
 1105         LIBELEC_SERIALIZE_DATA_V(&comp->batt, ser, 
"%s/%s/gen",
 
 1106             prefix, comp->info->name);
 
 1109         LIBELEC_SERIALIZE_DATA_V(&comp->batt, ser, 
"%s/%s/load",
 
 1110             prefix, comp->info->name);
 
 1113         LIBELEC_SERIALIZE_DATA_V(&comp->scb, ser, 
"%s/%s/cb",
 
 1114             prefix, comp->info->name);
 
 1120         mutex_enter(&comp->tie.lock);
 
 1121         conf_set_data_v(ser, 
"%s/%s/cur_state", comp->tie.cur_state,
 
 1122             comp->n_links * sizeof (*comp->tie.cur_state),
 
 1123             prefix, comp->info->name);
 
 1124         mutex_exit(&comp->tie.lock);
 
 1132 elec_comp_deserialize(elec_comp_t *comp, 
const conf_t *ser, 
const char *prefix)
 
 1134     ASSERT(comp != NULL);
 
 1135     ASSERT(comp->info != NULL);
 
 1136     ASSERT(comp->info->name != NULL);
 
 1137     ASSERT(ser != NULL);
 
 1138     ASSERT(prefix != NULL);
 
 1140     LIBELEC_DESERIALIZE_DATA_V(comp, ser, 
"%s/%s/data",
 
 1141         prefix, comp->info->name);
 
 1143     switch (comp->info->type) {
 
 1145         LIBELEC_DESERIALIZE_DATA_V(&comp->batt, ser, 
"%s/%s/batt",
 
 1146             prefix, comp->info->name);
 
 1149         LIBELEC_DESERIALIZE_DATA_V(&comp->batt, ser, 
"%s/%s/gen",
 
 1150             prefix, comp->info->name);
 
 1153         LIBELEC_DESERIALIZE_DATA_V(&comp->batt, ser, 
"%s/%s/load",
 
 1154             prefix, comp->info->name);
 
 1157         LIBELEC_DESERIALIZE_DATA_V(&comp->scb, ser, 
"%s/%s/cb",
 
 1158             prefix, comp->info->name);
 
 1164         mutex_enter(&comp->tie.lock);
 
 1165         if (conf_get_data_v(ser, 
"%s/%s/cur_state", comp->tie.cur_state,
 
 1166             comp->n_links * sizeof (*comp->tie.cur_state),
 
 1167             prefix, comp->info->name) !=
 
 1168             comp->n_links * sizeof (*comp->tie.cur_state)) {
 
 1169             mutex_exit(&comp->tie.lock);
 
 1172         mutex_exit(&comp->tie.lock);
 
 1207     ASSERT(sys != NULL);
 
 1208     ASSERT(ser != NULL);
 
 1209     ASSERT(prefix != NULL);
 
 1210 #ifdef  LIBELEC_WITH_NETLINK 
 1211     ASSERT(!sys->net_recv.active);
 
 1213     conf_set_data_v(ser, 
"%s/conf_crc64", &sys->conf_crc,
 
 1214         sizeof (sys->conf_crc), prefix);
 
 1216     mutex_enter(&sys->worker_interlock);
 
 1218     for (elec_comp_t *comp = list_head(&sys->comps); comp != NULL;
 
 1219         comp = list_next(&sys->comps, comp)) {
 
 1220         elec_comp_serialize(comp, ser, prefix);
 
 1223     mutex_exit(&sys->worker_interlock);
 
 1249     ASSERT(sys != NULL);
 
 1250     ASSERT(ser != NULL);
 
 1251     ASSERT(prefix != NULL);
 
 1252 #ifdef  LIBELEC_WITH_NETLINK 
 1253     ASSERT(!sys->net_recv.active);
 
 1255     if (!conf_get_data_v(ser, 
"%s/conf_crc64", &crc, 
sizeof (crc),
 
 1257         logMsg(
"Cannot deserialize libelec state: missing required " 
 1258             "state key %s/conf_crc64", prefix);
 
 1261     if (crc != sys->conf_crc) {
 
 1262         logMsg(
"Cannot deserialize libelec state: configuration " 
 1263             "file CRC mismatch");
 
 1266     mutex_enter(&sys->worker_interlock);
 
 1268     for (elec_comp_t *comp = list_head(&sys->comps); comp != NULL;
 
 1269         comp = list_next(&sys->comps, comp)) {
 
 1270         if (!elec_comp_deserialize(comp, ser, prefix)) {
 
 1271             logMsg(
"Failed to deserialize %s: malformed state",
 
 1276     mutex_exit(&sys->worker_interlock);
 
 1293     ASSERT(sys != NULL);
 
 1296     ASSERT(!sys->started);
 
 1298 #ifdef  LIBELEC_WITH_NETLINK 
 1299     libelec_disable_net_send(sys);
 
 1300     libelec_disable_net_recv(sys);
 
 1304     while ((ucbi = avl_destroy_nodes(&sys->user_cbs, &cookie)) != NULL)
 
 1306     avl_destroy(&sys->user_cbs);
 
 1307     mutex_destroy(&sys->user_cbs_lock);
 
 1310     while (avl_destroy_nodes(&sys->info2comp, &cookie) != NULL)
 
 1312     avl_destroy(&sys->info2comp);
 
 1315     while (avl_destroy_nodes(&sys->name2comp, &cookie) != NULL)
 
 1317     avl_destroy(&sys->info2comp);
 
 1319     while (list_remove_head(&sys->gens_batts) != NULL)
 
 1321     list_destroy(&sys->gens_batts);
 
 1323     while (list_remove_head(&sys->ties) != NULL)
 
 1325     list_destroy(&sys->ties);
 
 1327     while ((comp = list_remove_head(&sys->comps)) != NULL)
 
 1329     list_destroy(&sys->comps);
 
 1330     free(sys->comps_array);
 
 1332     mutex_destroy(&sys->worker_interlock);
 
 1333     mutex_destroy(&sys->paused_lock);
 
 1335     infos_free(sys->comp_infos, sys->num_infos);
 
 1337     free(sys->conf_filename);
 
 1339     (void)XPLMUnregisterDrawCallback(elec_draw_cb,
 
 1340         xplm_Phase_Window, 0, sys);
 
 1343     memset(sys, 0, 
sizeof (*sys));
 
 1347 #ifdef  LIBELEC_WITH_LIBSWITCH 
 1350 libelec_create_cb_switches(
const elec_sys_t *sys, 
const char *prefix,
 
 1353     ASSERT(sys != NULL);
 
 1354     ASSERT(prefix != NULL);
 
 1356     for (elec_comp_t *comp = list_head(&sys->comps); comp != NULL;
 
 1357         comp = list_next(&sys->comps, comp)) {
 
 1358         ASSERT(comp->info != NULL);
 
 1359         if (comp->info->type == 
ELEC_CB) {
 
 1360             char name[128], desc[128];
 
 1362             VERIFY3S(snprintf(name, 
sizeof (name), 
"%s%s",
 
 1363                 prefix, comp->info->name), <, sizeof (name));
 
 1364             VERIFY3S(snprintf(desc, 
sizeof (desc),
 
 1365                 "Circuit breaker %s", comp->info->name), <,
 
 1367             comp->scb.sw = libswitch_add_toggle(name, desc,
 
 1370             libswitch_set_anim_offset(comp->scb.sw, -1, 1);
 
 1371             libswitch_set(comp->scb.sw, 0);
 
 1372             libswitch_button_set_turn_on_delay(comp->scb.sw,
 
 1380 #ifdef  LIBELEC_SLOW_DEBUG 
 1383 libelec_step(elec_sys_t *sys)
 
 1385     ASSERT(sys != NULL);
 
 1386     worker_wake_up(&sys->worker);
 
 1424     const char *slot_qual)
 
 1426     switch (info->type) {
 
 1429         if (slot_qual == NULL)
 
 1431         if (strcmp(slot_qual, 
"AC") == 0) {
 
 1432             if (info->tru.ac != NULL)
 
 1434             info->tru.ac = info2;
 
 1435         } 
else if (strcmp(slot_qual, 
"DC") == 0) {
 
 1436             if (info->tru.dc != NULL)
 
 1438             info->tru.dc = info2;
 
 1444         if (slot_qual == NULL)
 
 1446         if (strcmp(slot_qual, 
"IN") == 0) {
 
 1447             if (info->xfrmr.input != NULL) {
 
 1450             info->xfrmr.input = info2;
 
 1451         } 
else if (strcmp(slot_qual, 
"OUT") == 0) {
 
 1452             if (info->xfrmr.output != NULL) {
 
 1455             info->xfrmr.output = info2;
 
 1461         info->bus.comps = safe_realloc(info->bus.comps,
 
 1462             (info->bus.n_comps + 1) * sizeof (*info->bus.comps));
 
 1463         info->bus.comps[info->bus.n_comps] = info2;
 
 1464         info->bus.n_comps++;
 
 1467         if (slot_qual == NULL)
 
 1469         if (strcmp(slot_qual, 
"IN") == 0) {
 
 1470             if (info->diode.sides[0] != NULL)
 
 1472             info->diode.sides[0] = info2;
 
 1474             if (strcmp(slot_qual, 
"OUT") != 0)
 
 1476             if (info->diode.sides[1] != NULL)
 
 1478             info->diode.sides[1] = info2;
 
 1489 find_comp_info(
elec_comp_info_t *infos, 
size_t num_comps, 
const char *name)
 
 1491     for (
unsigned i = 0; i < num_comps; i++) {
 
 1492         if (infos[i].name != NULL && strcmp(infos[i].name, name) == 0)
 
 1498 static gui_load_type_t
 
 1499 str2load_type(
const char *str)
 
 1501     ASSERT(str != NULL);
 
 1502     if (strcmp(str, 
"MOTOR") == 0)
 
 1503         return (GUI_LOAD_MOTOR);
 
 1504     return (GUI_LOAD_GENERIC);
 
 1508 infos_parse(
const char *filename, 
size_t *num_infos)
 
 1510 #define MAX_BUS_UNIQ    256 
 1511     uint64_t bus_IDs_seen[256] = { 0 };
 
 1512     unsigned bus_ID_cur = 0;
 
 1514     size_t comp_i = 0, num_comps = 0;
 
 1519     unsigned linenum = 0;
 
 1521     ASSERT(filename != NULL);
 
 1522     ASSERT(num_infos != NULL);
 
 1524     fp = fopen(filename, 
"r");
 
 1526         logMsg(
"Can't open electrical network file %s: %s",
 
 1527             filename, strerror(errno));
 
 1532     while (parser_get_next_line(fp, &line, &linecap, &linenum) > 0) {
 
 1533         if (strncmp(line, 
"BATT ", 5) == 0 ||
 
 1534             strncmp(line, 
"GEN ", 4) == 0 ||
 
 1535             strncmp(line, 
"TRU ", 4) == 0 ||
 
 1536             strncmp(line, 
"INV ", 4) == 0 ||
 
 1537             strncmp(line, 
"XFRMR ", 4) == 0 ||
 
 1538             strncmp(line, 
"LOAD ", 5) == 0 ||
 
 1539             strncmp(line, 
"BUS ", 4) == 0 ||
 
 1540             strncmp(line, 
"CB ", 3) == 0 ||
 
 1541             strncmp(line, 
"CB3 ", 4) == 0 ||
 
 1542             strncmp(line, 
"SHUNT ", 6) == 0 ||
 
 1543             strncmp(line, 
"TIE ", 4) == 0 ||
 
 1544             strncmp(line, 
"DIODE ", 6) == 0 ||
 
 1545             strncmp(line, 
"LABEL_BOX ", 10) == 0) {
 
 1547         } 
else if (strncmp(line, 
"LOADCB ", 7) == 0 ||
 
 1548             strncmp(line, 
"LOADCB3 ", 8) == 0) {
 
 1554     infos = safe_calloc(num_comps, 
sizeof (*infos));
 
 1555     for (
size_t i = 0; i < num_comps; i++) {
 
 1557         info->gui.pos = NULL_VECT2;
 
 1561     while (parser_get_next_line(fp, &line, &linecap, &linenum) > 0) {
 
 1566 #define INVALID_LINE_FOR_COMP_TYPE \ 
 1568         logMsg("%s:%d: invalid %s line for component of type %s", \
 
 1569             filename, linenum, cmd, comp_type2str(info->type)); \
 
 1570         free_strlist(comps, n_comps); \
 
 1573 #define CHECK_DUP_NAME(__name__) \ 
 1575         elec_comp_info_t *info2 = find_comp_info(infos, comp_i, \ 
 1577         if (info2 != NULL) { \ 
 1578             logMsg("%s:%d: duplicate component name %s " \
 
 1579                 "(previously found on line %d)", \
 
 1580                 filename, linenum, (__name__), \
 
 1581                 info2->parse_linenum); \
 
 1582             free_strlist(comps, n_comps); \
 
 1586 #define CHECK_COMP(cond, reason) \ 
 1589             logMsg("%s:%d: %s", filename, linenum, (reason)); \
 
 1590             free_strlist(comps, n_comps); \
 
 1594 #define CHECK_COMP_V(cond, reason, ...) \ 
 1597             logMsg("%s:%d: " reason, filename, linenum, \
 
 1599             free_strlist(comps, n_comps); \
 
 1604         comps = strsplit(line, 
" ", 
true, &n_comps);
 
 1606         if (strcmp(cmd, 
"BATT") == 0 && n_comps == 2) {
 
 1607             ASSERT3U(comp_i, <, num_comps);
 
 1608             CHECK_DUP_NAME(comps[1]);
 
 1609             info = &infos[comp_i++];
 
 1610             info->parse_linenum = linenum;
 
 1612             info->name = safe_strdup(comps[1]);
 
 1614         } 
else if (strcmp(cmd, 
"GEN") == 0 && n_comps == 2) {
 
 1615             ASSERT3U(comp_i, <, num_comps);
 
 1616             CHECK_DUP_NAME(comps[1]);
 
 1617             info = &infos[comp_i++];
 
 1618             info->parse_linenum = linenum;
 
 1620             info->name = safe_strdup(comps[1]);
 
 1622         } 
else if (strcmp(cmd, 
"TRU") == 0 && n_comps == 2) {
 
 1623             ASSERT3U(comp_i, <, num_comps);
 
 1624             CHECK_DUP_NAME(comps[1]);
 
 1625             info = &infos[comp_i++];
 
 1626             info->parse_linenum = linenum;
 
 1628             info->name = safe_strdup(comps[1]);
 
 1630         } 
else if (strcmp(cmd, 
"INV") == 0 && n_comps == 2) {
 
 1631             ASSERT3U(comp_i, <, num_comps);
 
 1632             CHECK_DUP_NAME(comps[1]);
 
 1633             info = &infos[comp_i++];
 
 1634             info->parse_linenum = linenum;
 
 1636             info->name = safe_strdup(comps[1]);
 
 1638         } 
else if (strcmp(cmd, 
"XFRMR") == 0 && n_comps == 2) {
 
 1639             ASSERT3U(comp_i, <, num_comps);
 
 1640             CHECK_DUP_NAME(comps[1]);
 
 1641             info = &infos[comp_i++];
 
 1642             info->parse_linenum = linenum;
 
 1644             info->name = safe_strdup(comps[1]);
 
 1646         } 
else if (strcmp(cmd, 
"LOAD") == 0 &&
 
 1647             (n_comps == 2 || n_comps == 3)) {
 
 1648             ASSERT3U(comp_i, <, num_comps);
 
 1649             CHECK_DUP_NAME(comps[1]);
 
 1650             info = &infos[comp_i++];
 
 1651             info->parse_linenum = linenum;
 
 1653             info->name = safe_strdup(comps[1]);
 
 1655                 info->load.ac = (strcmp(comps[2], 
"AC") == 0);
 
 1656         } 
else if (strcmp(cmd, 
"BUS") == 0 && n_comps == 3) {
 
 1657             ASSERT3U(comp_i, <, num_comps);
 
 1658             CHECK_DUP_NAME(comps[1]);
 
 1659             info = &infos[comp_i++];
 
 1660             info->parse_linenum = linenum;
 
 1662             info->name = safe_strdup(comps[1]);
 
 1663             info->bus.ac = (strcmp(comps[2], 
"AC") == 0);
 
 1664             memset(bus_IDs_seen, 0, 
sizeof (bus_IDs_seen));
 
 1666         } 
else if ((strcmp(cmd, 
"CB") == 0 ||
 
 1667             strcmp(cmd, 
"CB3") == 0) && n_comps == 3) {
 
 1668             ASSERT3U(comp_i, <, num_comps);
 
 1669             CHECK_DUP_NAME(comps[1]);
 
 1670             info = &infos[comp_i++];
 
 1671             info->parse_linenum = linenum;
 
 1673             info->name = safe_strdup(comps[1]);
 
 1675             info->cb.max_amps = atof(comps[2]);
 
 1676             info->cb.triphase = (strcmp(cmd, 
"CB3") == 0);
 
 1677         } 
else if (strcmp(cmd, 
"SHUNT") == 0 && n_comps == 2) {
 
 1678             ASSERT3U(comp_i, <, num_comps);
 
 1679             CHECK_DUP_NAME(comps[1]);
 
 1680             info = &infos[comp_i++];
 
 1681             info->parse_linenum = linenum;
 
 1683             info->name = safe_strdup(comps[1]);
 
 1684         } 
else if (strcmp(cmd, 
"TIE") == 0 && n_comps == 2) {
 
 1685             ASSERT3U(comp_i, <, num_comps);
 
 1686             CHECK_DUP_NAME(comps[1]);
 
 1687             info = &infos[comp_i++];
 
 1688             info->parse_linenum = linenum;
 
 1690             info->name = safe_strdup(comps[1]);
 
 1691         } 
else if (strcmp(cmd, 
"DIODE") == 0 && n_comps == 2) {
 
 1692             ASSERT3U(comp_i, <, num_comps);
 
 1693             CHECK_DUP_NAME(comps[1]);
 
 1694             info = &infos[comp_i++];
 
 1695             info->parse_linenum = linenum;
 
 1697             info->name = safe_strdup(comps[1]);
 
 1698         } 
else if (strcmp(cmd, 
"LABEL_BOX") == 0 && n_comps >= 7) {
 
 1700             ASSERT3U(comp_i, <, num_comps);
 
 1701             info = &infos[comp_i++];
 
 1702             info->parse_linenum = linenum;
 
 1704             info->label_box.pos =
 
 1705                 VECT2(atof(comps[1]), atof(comps[2]));
 
 1706             info->label_box.sz =
 
 1707                 VECT2(atof(comps[3]), atof(comps[4]));
 
 1708             info->label_box.font_scale = atof(comps[5]);
 
 1709             for (
size_t i = 6; i < n_comps; i++) {
 
 1710                 append_format(&info->name, &sz, 
"%s%s",
 
 1711                     comps[i], i + 1 < n_comps ? 
" " : 
"");
 
 1713         } 
else if (strcmp(cmd, 
"VOLTS") == 0 && n_comps == 2 &&
 
 1716                 info->batt.volts = atof(comps[1]);
 
 1717                 CHECK_COMP(info->batt.volts > 0,
 
 1718                     "battery voltage must be positive");
 
 1719             } 
else if (info->type == 
ELEC_GEN) {
 
 1720                 info->gen.volts = atof(comps[1]);
 
 1721                 CHECK_COMP(info->gen.volts > 0,
 
 1722                     "generator voltage must be positive");
 
 1724                 INVALID_LINE_FOR_COMP_TYPE;
 
 1726         } 
else if (strcmp(cmd, 
"FREQ") == 0 && n_comps == 2 &&
 
 1727             info != NULL && info->type == 
ELEC_GEN) {
 
 1728             info->gen.freq = atof(comps[1]);
 
 1729             CHECK_COMP(info->gen.freq >= 0,
 
 1730                 "frequency must be a non-negative number");
 
 1731         } 
else if (strcmp(cmd, 
"OUT_FREQ") == 0 && n_comps == 2 &&
 
 1732             info != NULL && info->type == 
ELEC_INV) {
 
 1733             info->tru.out_freq = atof(comps[1]);
 
 1734             CHECK_COMP(info->tru.out_freq > 0,
 
 1735                 "frequency must be a positive number");
 
 1736         } 
else if (strcmp(cmd, 
"IN_VOLTS") == 0 && n_comps == 2 &&
 
 1737             info != NULL && (info->type == 
ELEC_TRU ||
 
 1739             info->tru.in_volts = atof(comps[1]);
 
 1740         } 
else if (strcmp(cmd, 
"IN_VOLTS") == 0 && n_comps == 2 &&
 
 1742             info->xfrmr.in_volts = atof(comps[1]);
 
 1743         } 
else if (strcmp(cmd, 
"OUT_VOLTS") == 0 && n_comps == 2 &&
 
 1744             info != NULL && (info->type == 
ELEC_TRU ||
 
 1746             info->tru.out_volts = atof(comps[1]);
 
 1747         } 
else if (strcmp(cmd, 
"OUT_VOLTS") == 0 && n_comps == 2 &&
 
 1749             info->xfrmr.out_volts = atof(comps[1]);
 
 1750         } 
else if (strcmp(cmd, 
"MIN_VOLTS") == 0 && n_comps == 2 &&
 
 1751             info != NULL && info->type == 
ELEC_LOAD) {
 
 1752             info->load.min_volts = atof(comps[1]);
 
 1753         } 
else if (strcmp(cmd, 
"MIN_VOLTS") == 0 && n_comps == 2 &&
 
 1754             info != NULL && (info->type == 
ELEC_TRU ||
 
 1756             info->tru.min_volts = atof(comps[1]);
 
 1757             CHECK_COMP(info->tru.min_volts < info->tru.out_volts,
 
 1758                 "minimum voltage must be lower than nominal " 
 1760         } 
else if (strcmp(cmd, 
"INCAP") == 0 &&
 
 1761             (n_comps == 3 || n_comps == 4) &&
 
 1762             info != NULL && info->type == 
ELEC_LOAD) {
 
 1763             info->load.incap_C = atof(comps[1]);
 
 1764             CHECK_COMP_V(info->load.incap_C > 0, 
"invalid input " 
 1765                 "capacitance %s: must be positive (in Farads)",
 
 1767             info->load.incap_R = atof(comps[2]);
 
 1768             CHECK_COMP_V(info->load.incap_R > 0, 
"invalid input " 
 1769                 "capacitance internal resistance %s: must be " 
 1770                 "positive (in Ohms)", comps[2]);
 
 1772                 info->load.incap_leak_Qps = atof(comps[3]);
 
 1774                 info->load.incap_leak_Qps =
 
 1775                     info->load.incap_C / 200;
 
 1777         } 
else if (strcmp(cmd, 
"CAPACITY") == 0 && n_comps == 2 &&
 
 1778             info != NULL && info->type == 
ELEC_BATT) {
 
 1779             info->batt.capacity = atof(comps[1]);
 
 1780             CHECK_COMP(info->batt.capacity > 0, 
"battery CAPACITY " 
 1781                 "must be positive (in Watt-Hours)");
 
 1782         } 
else if (strcmp(cmd, 
"STAB") == 0 && n_comps == 2 &&
 
 1783             info != NULL && info->type == 
ELEC_LOAD) {
 
 1784             info->load.stab = (strcmp(comps[1], 
"TRUE") == 0);
 
 1785         } 
else if (strcmp(cmd, 
"STAB_RATE") == 0 && n_comps == 2 &&
 
 1786             info != NULL && info->type == 
ELEC_GEN) {
 
 1787             info->gen.stab_rate_U = atof(comps[1]);
 
 1788         } 
else if (strcmp(cmd, 
"STAB_RATE_F") == 0 && n_comps == 2 &&
 
 1789             info != NULL && info->type == 
ELEC_GEN) {
 
 1790             CHECK_COMP(info->gen.freq != 0, 
"cannot define " 
 1791                 "frequency stabilization rate for DC generators, " 
 1792                 "or you must place the FREQ line before the " 
 1793                 "STAB_RATE_F line");
 
 1794             info->gen.stab_rate_f = atof(comps[1]);
 
 1795         } 
else if (strcmp(cmd, 
"EXC_RPM") == 0 && n_comps == 2 &&
 
 1796             info != NULL && info->type == 
ELEC_GEN) {
 
 1797             info->gen.exc_rpm = atof(comps[1]);
 
 1798             CHECK_COMP(info->gen.exc_rpm >= 0,
 
 1799                 "excitation rpm must be non-negative");
 
 1800         } 
else if (strcmp(cmd, 
"MIN_RPM") == 0 && n_comps == 2 &&
 
 1801             info != NULL && info->type == 
ELEC_GEN) {
 
 1802             info->gen.min_rpm = atof(comps[1]);
 
 1803             CHECK_COMP(info->gen.min_rpm > 0,
 
 1804                 "generator MIN_RPM must be positive");
 
 1805         } 
else if (strcmp(cmd, 
"MAX_RPM") == 0 && n_comps == 2 &&
 
 1806             info != NULL && info->type == 
ELEC_GEN) {
 
 1807             info->gen.max_rpm = atof(comps[1]);
 
 1808             CHECK_COMP(info->gen.max_rpm > 0,
 
 1809                 "generator MAX_RPM must be positive");
 
 1810         } 
else if (strcmp(cmd, 
"RATE") == 0 && n_comps == 2 &&
 
 1811             info != NULL && info->type == 
ELEC_CB) {
 
 1812             info->cb.rate = clamp(atof(comps[1]), 0.001, 1000);
 
 1813         } 
else if (strcmp(cmd, 
"MAX_PWR") == 0 && n_comps == 2 &&
 
 1814             info != NULL && info->type == 
ELEC_BATT) {
 
 1815             info->batt.max_pwr = atof(comps[1]);
 
 1816             CHECK_COMP(info->batt.max_pwr > 0,
 
 1817                 "MAX_PWR must be positive (in Watts)");
 
 1818         } 
else if (strcmp(cmd, 
"CURVEPT") == 0 && n_comps == 4 &&
 
 1823             switch (info->type) {
 
 1825                 curve_pp = &info->gen.eff_curve;
 
 1829                 curve_pp = &info->tru.eff_curve;
 
 1832                 curve_pp = &info->xfrmr.eff_curve;
 
 1835                 INVALID_LINE_FOR_COMP_TYPE;
 
 1838             if (*curve_pp != NULL) {
 
 1839                 for (vect2_t *curve = *curve_pp;
 
 1840                     !IS_NULL_VECT(*curve); curve++)
 
 1845             *curve_pp = safe_realloc(*curve_pp,
 
 1846                 (num + 2) * 
sizeof (vect2_t));
 
 1848                 VECT2(atof(comps[2]), atof(comps[3]));
 
 1849             (*curve_pp)[num + 1] = NULL_VECT2;
 
 1850         } 
else if (strcmp(cmd, 
"ENDPT") == 0 && info != NULL &&
 
 1851             info->type == 
ELEC_BUS && (n_comps == 2 || n_comps == 3)) {
 
 1852             uint64_t cur_ID = crc64(comps[1], strlen(comps[1]));
 
 1854                 find_comp_info(infos, num_comps, comps[1]);
 
 1856             if (info2 == NULL) {
 
 1857                 logMsg(
"%s:%d: unknown component %s",
 
 1858                     filename, linenum, comps[1]);
 
 1859                 free_strlist(comps, n_comps);
 
 1862             for (
unsigned i = 0; i < bus_ID_cur; i++) {
 
 1863                 if (bus_IDs_seen[i] == cur_ID) {
 
 1864                     logMsg(
"%s:%d: duplicate endpoint %s",
 
 1865                         filename, linenum, comps[1]);
 
 1866                     free_strlist(comps, n_comps);
 
 1870             if (bus_ID_cur < MAX_BUS_UNIQ)
 
 1871                 bus_IDs_seen[bus_ID_cur++] = cur_ID;
 
 1872             if (!add_info_link(info, info2,
 
 1873                 (n_comps == 3 ? comps[2] : NULL)) ||
 
 1874                 !add_info_link(info2, info,
 
 1875                 (n_comps == 3 ? comps[2] : NULL))) {
 
 1876                 logMsg(
"%s:%d: bad component link line",
 
 1878                 free_strlist(comps, n_comps);
 
 1881         } 
else if ((strcmp(cmd, 
"LOADCB") == 0 ||
 
 1882             strcmp(cmd, 
"LOADCB3") == 0) && (n_comps == 2 ||
 
 1883             n_comps == 3) && info != NULL && info->type == 
ELEC_LOAD) {
 
 1886             ASSERT3U(comp_i + 1, <, num_comps);
 
 1887             cb = &infos[comp_i++];
 
 1888             cb->parse_linenum = linenum;
 
 1890             cb->name = sprintf_alloc(
"CB_%s", info->name);
 
 1892             cb->cb.max_amps = atof(comps[1]);
 
 1893             cb->cb.triphase = (strcmp(cmd, 
"LOADCB3") == 0);
 
 1896                 strlcpy(cb->location, comps[2],
 
 1897                     sizeof (cb->location));
 
 1900             bus = &infos[comp_i++];
 
 1901             bus->parse_linenum = linenum;
 
 1903             bus->name = sprintf_alloc(
"CB_BUS_%s", info->name);
 
 1904             bus->bus.ac = info->load.ac;
 
 1905             bus->autogen = 
true;
 
 1907             VERIFY(add_info_link(bus, info, NULL));
 
 1908             VERIFY(add_info_link(bus, cb, NULL));
 
 1909             ASSERT_MSG(!cb->cb.triphase || info->load.ac,
 
 1910                 "Can't connect 3-phase CB %s to a DC bus",
 
 1912         } 
else if (strcmp(cmd, 
"STD_LOAD") == 0 && n_comps == 2 &&
 
 1913             info != NULL && info->type == 
ELEC_LOAD) {
 
 1914             info->load.std_load = atof(comps[1]);
 
 1915         } 
else if (strcmp(cmd, 
"FUSE") == 0 && n_comps == 1 &&
 
 1916             info != NULL && info->type == 
ELEC_CB) {
 
 1917             info->cb.fuse = 
true;
 
 1918         } 
else if (strcmp(cmd, 
"GUI_POS") == 0 && (n_comps == 3 ||
 
 1919             n_comps == 4) && info != NULL) {
 
 1920             info->gui.pos = VECT2(atof(comps[1]), atof(comps[2]));
 
 1922                 info->gui.sz = atof(comps[3]);
 
 1925         } 
else if (strcmp(cmd, 
"GUI_ROT") == 0 && n_comps == 2 &&
 
 1927             info->gui.rot = atof(comps[1]);
 
 1928         } 
else if (strcmp(cmd, 
"GUI_LOAD") == 0 && n_comps == 2 &&
 
 1930             info->gui.load_type = str2load_type(comps[1]);
 
 1931         } 
else if (strcmp(cmd, 
"GUI_VIRT") == 0 && n_comps == 1 &&
 
 1933             info->gui.virt = 
true;
 
 1934         } 
else if (strcmp(cmd, 
"GUI_INVIS") == 0 && n_comps == 1 &&
 
 1936             info->gui.invis = 
true;
 
 1937         } 
else if (strcmp(cmd, 
"GUI_COLOR") == 0 && n_comps == 4 &&
 
 1939             info->gui.color = VECT3(atof(comps[1]),
 
 1940                 atof(comps[2]), atof(comps[3]));
 
 1941         } 
else if (strcmp(cmd, 
"CHGR_BATT") == 0 && n_comps == 4 &&
 
 1942             info != NULL && info->type == 
ELEC_TRU) {
 
 1943             info->tru.charger = 
true;
 
 1945                 find_comp_info(infos, num_comps, comps[1]);
 
 1946             if (info->tru.batt == NULL) {
 
 1947                 logMsg(
"%s:%d: unknown component %s",
 
 1948                     filename, linenum, comps[1]);
 
 1949                 free_strlist(comps, n_comps);
 
 1952             info->tru.batt_conn =
 
 1953                 find_comp_info(infos, num_comps, comps[2]);
 
 1954             if (info->tru.batt_conn == NULL) {
 
 1955                 logMsg(
"%s:%d: unknown component %s",
 
 1956                     filename, linenum, comps[1]);
 
 1957                 free_strlist(comps, n_comps);
 
 1960             info->tru.curr_lim = atof(comps[3]);
 
 1961             if (info->tru.curr_lim <= 0) {
 
 1962                 logMsg(
"%s:%d: current limit must be positive",
 
 1964                 free_strlist(comps, n_comps);
 
 1967         } 
else if (strcmp(cmd, 
"CHG_R") == 0 && n_comps == 2 &&
 
 1968             info != NULL && info->type == 
ELEC_BATT) {
 
 1969             info->batt.chg_R = atof(comps[1]);
 
 1970             CHECK_COMP(info->batt.chg_R > 0,
 
 1971                 "charge resistance must be positive");
 
 1972         } 
else if (strcmp(cmd, 
"INT_R") == 0 && n_comps == 2 &&
 
 1973             info != NULL && (info->type == 
ELEC_BATT ||
 
 1976             info->int_R = atof(comps[1]);
 
 1977             CHECK_COMP(info->int_R > 0,
 
 1978                 "internal resistance must be positive");
 
 1979         } 
else if (strcmp(cmd, 
"LOCATION") == 0 && n_comps == 2 &&
 
 1981             strlcpy(info->location, comps[1],
 
 1982                 sizeof (info->location));
 
 1984             logMsg(
"%s:%d: unknown or malformed line",
 
 1986             free_strlist(comps, n_comps);
 
 1989         free_strlist(comps, n_comps);
 
 1992     if (!validate_elec_comp_infos_parse(infos, num_comps, filename))
 
 1995 #undef  INVALID_LINE_FOR_COMP_TYPE 
 1996 #undef  CHECK_DUP_NAME 
 2002     *num_infos = num_comps;
 
 2016     ASSERT(infos != NULL || num_infos == 0);
 
 2017     for (
size_t i = 0; i < num_infos; i++) {
 
 2022             free(info->gen.eff_curve);
 
 2024             free(info->tru.eff_curve);
 
 2026             free(info->xfrmr.eff_curve);
 
 2028             ZERO_FREE_N(info->bus.comps, info->bus.n_comps);
 
 2030     ZERO_FREE_N(infos, num_infos);
 
 2054     ASSERT(sys != NULL);
 
 2059     info->userinfo = userinfo;
 
 2061     mutex_enter(&sys->user_cbs_lock);
 
 2062     VERIFY3P(avl_find(&sys->user_cbs, info, &where), ==, NULL);
 
 2063     avl_insert(&sys->user_cbs, info, where);
 
 2064     mutex_exit(&sys->user_cbs_lock);
 
 2081     ASSERT(sys != NULL);
 
 2086     srch.userinfo = userinfo;
 
 2088     mutex_enter(&sys->user_cbs_lock);
 
 2089     info = avl_find(&sys->user_cbs, &srch, NULL);
 
 2090     VERIFY(info != NULL);
 
 2091     avl_remove(&sys->user_cbs, info);
 
 2092     mutex_exit(&sys->user_cbs_lock);
 
 2111     ASSERT(sys != NULL);
 
 2119     for (elec_comp_t *comp = list_head(&sys->comps); comp != NULL;
 
 2120         comp = list_next(&sys->comps, comp)) {
 
 2133     ASSERT(comp != NULL);
 
 2135     return (comp->info);
 
 2150     const elec_comp_t srch_comp = { .info = &srch_info };
 
 2152     ASSERT(sys != NULL);
 
 2154     return (avl_find(&sys->name2comp, &srch_comp, NULL));
 
 2166     ASSERT(comp != NULL);
 
 2167     ASSERT(comp->info != NULL);
 
 2170     switch (comp->info->type) {
 
 2172         return (comp->n_links);
 
 2174         return (comp->n_links);
 
 2196     ASSERT(comp != NULL);
 
 2197     ASSERT(comp->info != NULL);
 
 2198     ASSERT3U(i, <, comp->n_links);
 
 2200     return (comp->links[i].comp);
 
 2236     ASSERT(comp != NULL);
 
 2238     NET_ADD_RECV_COMP(comp);
 
 2239     mutex_enter((mutex_t *)&comp->rw_ro_lock);
 
 2240     volts = comp->ro.in_volts;
 
 2241     mutex_exit((mutex_t *)&comp->rw_ro_lock);
 
 2280     ASSERT(comp != NULL);
 
 2282     NET_ADD_RECV_COMP(comp);
 
 2283     mutex_enter((mutex_t *)&comp->rw_ro_lock);
 
 2284     volts = comp->ro.out_volts;
 
 2285     mutex_exit((mutex_t *)&comp->rw_ro_lock);
 
 2327     ASSERT(comp != NULL);
 
 2329     NET_ADD_RECV_COMP(comp);
 
 2330     mutex_enter((mutex_t *)&comp->rw_ro_lock);
 
 2331     amps = comp->ro.in_amps * (1 - comp->ro.leak_factor);
 
 2332     mutex_exit((mutex_t *)&comp->rw_ro_lock);
 
 2364     ASSERT(comp != NULL);
 
 2366     NET_ADD_RECV_COMP(comp);
 
 2367     mutex_enter((mutex_t *)&comp->rw_ro_lock);
 
 2368     amps = comp->ro.out_amps * (1 - comp->ro.leak_factor);
 
 2369     mutex_exit((mutex_t *)&comp->rw_ro_lock);
 
 2414     ASSERT(comp != NULL);
 
 2416     NET_ADD_RECV_COMP(comp);
 
 2417     mutex_enter((mutex_t *)&comp->rw_ro_lock);
 
 2418     watts = comp->ro.in_pwr * (1 - comp->ro.leak_factor);
 
 2419     mutex_exit((mutex_t *)&comp->rw_ro_lock);
 
 2473     ASSERT(comp != NULL);
 
 2475     NET_ADD_RECV_COMP(comp);
 
 2476     mutex_enter((mutex_t *)&comp->rw_ro_lock);
 
 2477     watts = comp->ro.out_pwr * (1 - comp->ro.leak_factor);
 
 2478     mutex_exit((mutex_t *)&comp->rw_ro_lock);
 
 2508     ASSERT(comp != NULL);
 
 2510     NET_ADD_RECV_COMP(comp);
 
 2511     mutex_enter((mutex_t *)&comp->rw_ro_lock);
 
 2512     freq = comp->ro.in_freq;
 
 2513     mutex_exit((mutex_t *)&comp->rw_ro_lock);
 
 2550     ASSERT(comp != NULL);
 
 2552     NET_ADD_RECV_COMP(comp);
 
 2553     mutex_enter((mutex_t *)&comp->rw_ro_lock);
 
 2554     freq = comp->ro.out_freq;
 
 2555     mutex_exit((mutex_t *)&comp->rw_ro_lock);
 
 2576     ASSERT(comp != NULL);
 
 2577     ASSERT(comp->info != NULL);
 
 2578     ASSERT3U(comp->info->type, ==, 
ELEC_LOAD);
 
 2579     return (comp->load.incap_U);
 
 2592     ASSERT(comp != NULL);
 
 2593     ASSERT(comp->info != NULL);
 
 2594     switch (comp->info->type) {
 
 2603         return (comp->info->gen.freq != 0);
 
 2605         ASSERT(comp->links[0].comp != NULL);
 
 2606         return (comp->links[0].comp->info->bus.ac);
 
 2608         return (comp->info->bus.ac);
 
 2611         ASSERT(comp->links[0].comp != 0);
 
 2612         return (comp->links[0].comp->info->bus.ac);
 
 2615         ASSERT(comp->n_links != 0);
 
 2616         ASSERT(comp->links[0].comp != NULL);
 
 2617         return (comp->links[0].comp->info->bus.ac);
 
 2625 libelec_comp_get_type(
const elec_comp_t *comp)
 
 2627     ASSERT(comp != NULL);
 
 2628     return (comp->info->type);
 
 2632 libelec_comp_get_name(
const elec_comp_t *comp)
 
 2634     ASSERT(comp != NULL);
 
 2635     return (comp->info->name);
 
 2639 libelec_comp_get_location(
const elec_comp_t *comp)
 
 2641     ASSERT(comp != NULL);
 
 2642     return (comp->info->location);
 
 2646 libelec_comp_get_autogen(
const elec_comp_t *comp)
 
 2648     ASSERT(comp != NULL);
 
 2649     return (comp->info->autogen);
 
 2672     ASSERT(comp != NULL);
 
 2685     ASSERT(comp != NULL);
 
 2686     switch (comp->info->type) {
 
 2688         mutex_enter(&((elec_comp_t *)comp)->gen.lock);
 
 2689         eff = comp->gen.eff;
 
 2690         mutex_exit(&((elec_comp_t *)comp)->gen.lock);
 
 2694         eff = comp->tru.eff;
 
 2697         eff = comp->xfrmr.eff;
 
 2706 libelec_comp_get_srcs(
const elec_comp_t *comp,
 
 2707     elec_comp_t *srcs[CONST_ARRAY_LEN_ARG(ELEC_MAX_SRCS)])
 
 2709     ASSERT(comp != NULL);
 
 2710     ASSERT(srcs != NULL);
 
 2712     mutex_enter((mutex_t *)&comp->rw_ro_lock);
 
 2713     memcpy(srcs, comp->srcs_ext, sizeof (elec_comp_t *) * ELEC_MAX_SRCS);
 
 2714     mutex_exit((mutex_t *)&comp->rw_ro_lock);
 
 2716     for (
unsigned i = 0; i < ELEC_MAX_SRCS; i++) {
 
 2717         if (srcs[i] == NULL)
 
 2720     return (ELEC_MAX_SRCS);
 
 2744     ASSERT(comp != NULL);
 
 2745     mutex_enter(&comp->rw_ro_lock);
 
 2746     comp->ro.failed = failed;
 
 2747     mutex_exit(&comp->rw_ro_lock);
 
 2757     ASSERT(comp != NULL);
 
 2758     NET_ADD_RECV_COMP(comp);
 
 2759     return (comp->ro.failed);
 
 2777     ASSERT(comp != NULL);
 
 2778     mutex_enter(&comp->rw_ro_lock);
 
 2779     comp->ro.shorted = shorted;
 
 2780     mutex_exit(&comp->rw_ro_lock);
 
 2791     ASSERT(comp != NULL);
 
 2792     NET_ADD_RECV_COMP(comp);
 
 2793     return (comp->ro.shorted);
 
 2797 gen_set_random_param(elec_comp_t *comp, 
double *param, 
double norm_value,
 
 2802     ASSERT(comp != NULL);
 
 2803     ASSERT(param != NULL);
 
 2804     ASSERT3F(stddev, >=, 0);
 
 2807         new_param = norm_value + crc64_rand_normal(stddev);
 
 2813         if (new_param > norm_value) {
 
 2814             new_param = clamp(new_param, norm_value + 0.5 * stddev,
 
 2815                 norm_value + 1.5 * stddev);
 
 2817             new_param = clamp(new_param, norm_value - 1.5 * stddev,
 
 2818                 norm_value - 0.5 * stddev);
 
 2821         new_param = norm_value;
 
 2823     mutex_enter(&comp->sys->worker_interlock);
 
 2824     *param = norm_value;
 
 2825     mutex_exit(&comp->sys->worker_interlock);
 
 2851     ASSERT(comp != NULL);
 
 2852     ASSERT3U(comp->info->type, ==, 
ELEC_GEN);
 
 2853     ASSERT3F(1.5 * stddev, <, comp->info->gen.volts);
 
 2854     return (gen_set_random_param(comp, &comp->gen.tgt_volts,
 
 2855         comp->info->gen.volts, stddev));
 
 2866     ASSERT(comp != NULL);
 
 2867     ASSERT3U(comp->info->type, ==, 
ELEC_GEN);
 
 2869     ASSERT3F(comp->info->gen.freq - 1.5 * stddev, >, 0);
 
 2870     return (gen_set_random_param(comp, &comp->gen.tgt_freq,
 
 2871         comp->info->gen.freq, stddev));
 
 2884     ASSERT(comp != NULL);
 
 2885     ASSERT(!comp->sys->started);
 
 2886     comp->info->userinfo = userinfo;
 
 2897     ASSERT(comp != NULL);
 
 2898     return (comp->info->userinfo);
 
 2920     ASSERT(batt != NULL);
 
 2921     ASSERT3U(batt->info->type, ==, 
ELEC_BATT);
 
 2922     ASSERT(!batt->sys->started);
 
 2923     batt->info->batt.get_temp = cb;
 
 2935     ASSERT(batt != NULL);
 
 2936     ASSERT3U(batt->info->type, ==, 
ELEC_BATT);
 
 2937     return (batt->info->batt.get_temp);
 
 2958     ASSERT(gen != NULL);
 
 2959     ASSERT3U(gen->info->type, ==, 
ELEC_GEN);
 
 2960     ASSERT(!gen->sys->started);
 
 2961     gen->info->gen.get_rpm = cb;
 
 2972     ASSERT(gen != NULL);
 
 2973     ASSERT3U(gen->info->type, ==, 
ELEC_GEN);
 
 2974     return (gen->info->gen.get_rpm);
 
 2995     ASSERT(load != NULL);
 
 2996     ASSERT3U(load->info->type, ==, 
ELEC_LOAD);
 
 2997     ASSERT(!load->sys->started);
 
 2998     load->info->load.get_load = cb;
 
 3009     ASSERT(load != NULL);
 
 3010     ASSERT3U(load->info->type, ==, 
ELEC_LOAD);
 
 3011     return (load->info->load.get_load);
 
 3015 update_short_leak_factor(elec_comp_t *comp, 
double d_t)
 
 3017     ASSERT(comp != NULL);
 
 3018     ASSERT3F(d_t, >, 0);
 
 3020     if (comp->rw.shorted) {
 
 3026             if (comp->ro.in_pwr != 0)
 
 3027                 FILTER_IN(comp->rw.leak_factor, 0.99, d_t, 1);
 
 3029                 comp->rw.leak_factor = 0;
 
 3031             comp->rw.leak_factor =
 
 3032                 wavg(0.97, 0.975, crc64_rand_fract());
 
 3035         comp->rw.leak_factor = 0;
 
 3040 network_reset(elec_sys_t *sys, 
double d_t)
 
 3042     ASSERT(sys != NULL);
 
 3043     ASSERT_MUTEX_HELD(&sys->worker_interlock);
 
 3044     ASSERT3F(d_t, >, 0);
 
 3046     for (elec_comp_t *comp = list_head(&sys->comps); comp != NULL;
 
 3047         comp = list_next(&sys->comps, comp)) {
 
 3048         elec_comp_t *srcs_ext[ELEC_MAX_SRCS];
 
 3050         memcpy(srcs_ext, comp->srcs, sizeof (srcs_ext));
 
 3052         comp->rw.in_volts = 0;
 
 3053         comp->rw.in_pwr = 0;
 
 3054         comp->rw.in_amps = 0;
 
 3055         comp->rw.in_freq = 0;
 
 3056         comp->rw.out_volts = 0;
 
 3057         comp->rw.out_pwr = 0;
 
 3058         comp->rw.out_amps = 0;
 
 3059         comp->rw.out_freq = 0;
 
 3060         comp->rw.short_amps = 0;
 
 3061         comp->src_int_cond_total = 0;
 
 3062         memset(comp->srcs, 0, sizeof (comp->srcs));
 
 3064         for (
unsigned i = 0; i < comp->n_links; i++) {
 
 3065             memset(comp->links[i].out_amps, 0,
 
 3066                 sizeof (comp->links[i].out_amps));
 
 3069         mutex_enter(&comp->rw_ro_lock);
 
 3070         comp->rw.failed = comp->ro.failed;
 
 3071         comp->rw.shorted = comp->ro.shorted;
 
 3072         update_short_leak_factor(comp, d_t);
 
 3073         memcpy(comp->srcs_ext, srcs_ext, sizeof (comp->srcs_ext));
 
 3074         mutex_exit(&comp->rw_ro_lock);
 
 3076         comp->integ_mask = 0;
 
 3077         switch (comp->info->type) {
 
 3079             comp->load.seen = 
false;
 
 3083             mutex_enter(&comp->tie.lock);
 
 3084             memcpy(comp->tie.wk_state, comp->tie.cur_state,
 
 3085                 comp->n_links * sizeof (*comp->tie.wk_state));
 
 3086             mutex_exit(&comp->tie.lock);
 
 3089 #ifdef  LIBELEC_WITH_LIBSWITCH 
 3090             if (comp->scb.sw != NULL) {
 
 3092                     !libswitch_read(comp->scb.sw, NULL);
 
 3095             comp->scb.wk_set = comp->scb.cur_set;
 
 3104 network_update_gen(elec_comp_t *gen, 
double d_t)
 
 3106     ASSERT(gen != NULL);
 
 3107     ASSERT(gen->info != NULL);
 
 3108     ASSERT3U(gen->info->type, ==, 
ELEC_GEN);
 
 3110     if (gen->info->gen.get_rpm != NULL) {
 
 3111         double rpm = gen->info->gen.get_rpm(gen, gen->info->userinfo);
 
 3112         ASSERT(!isnan(rpm));
 
 3113         mutex_enter(&gen->gen.lock);
 
 3114         gen->gen.rpm = MAX(rpm, GEN_MIN_RPM);
 
 3115         mutex_exit(&gen->gen.lock);
 
 3117     if (gen->gen.rpm <= GEN_MIN_RPM) {
 
 3118         gen->gen.stab_factor_U = 1;
 
 3119         gen->gen.stab_factor_f = 1;
 
 3120         gen->rw.in_volts = 0;
 
 3121         gen->rw.in_freq = 0;
 
 3122         gen->rw.out_volts = 0;
 
 3123         gen->rw.out_freq = 0;
 
 3130     if (gen->info->gen.stab_rate_U > 0) {
 
 3131         double stab_factor_U = clamp(gen->gen.ctr_rpm / gen->gen.rpm,
 
 3132             gen->gen.min_stab_U, gen->gen.max_stab_U);
 
 3133         double stab_rate_mod =
 
 3134             clamp(1 + crc64_rand_normal(0.1), 0.1, 10);
 
 3135         FILTER_IN(gen->gen.stab_factor_U, stab_factor_U, d_t,
 
 3136             gen->info->gen.stab_rate_U * stab_rate_mod);
 
 3138         gen->gen.stab_factor_U = 1;
 
 3140     if (gen->info->gen.stab_rate_f > 0) {
 
 3141         double stab_factor_f = clamp(gen->gen.ctr_rpm / gen->gen.rpm,
 
 3142             gen->gen.min_stab_f, gen->gen.max_stab_f);
 
 3143         double stab_rate_mod =
 
 3144             clamp(1 + crc64_rand_normal(0.1), 0.1, 10);
 
 3145         FILTER_IN(gen->gen.stab_factor_f, stab_factor_f, d_t,
 
 3146             gen->info->gen.stab_rate_f * stab_rate_mod);
 
 3148         gen->gen.stab_factor_f = 1;
 
 3150     if (!gen->rw.failed) {
 
 3151         if (gen->gen.rpm < gen->info->gen.exc_rpm) {
 
 3152             gen->rw.in_volts = 0;
 
 3153             gen->rw.in_freq = 0;
 
 3155             ASSERT(gen->gen.tgt_volts != 0);
 
 3156             gen->rw.in_volts = (gen->gen.rpm / gen->gen.ctr_rpm) *
 
 3157                 gen->gen.stab_factor_U * gen->gen.tgt_volts;
 
 3158             if (gen->gen.tgt_freq != 0) {
 
 3159                 gen->rw.in_freq = (gen->gen.rpm /
 
 3161                     gen->gen.stab_factor_f * gen->gen.tgt_freq;
 
 3164         gen->rw.out_volts = gen->rw.in_volts;
 
 3165         gen->rw.out_freq = gen->rw.in_freq;
 
 3167         gen->rw.in_volts = 0;
 
 3168         gen->rw.in_freq = 0;
 
 3169         gen->rw.out_volts = 0;
 
 3170         gen->rw.out_freq = 0;
 
 3175 network_update_batt(elec_comp_t *batt, 
double d_t)
 
 3177     double U, J, temp_coeff, J_max, I_max, I_rel;
 
 3179     ASSERT(batt != NULL);
 
 3180     ASSERT(batt->info != NULL);
 
 3181     ASSERT3U(batt->info->type, ==, 
ELEC_BATT);
 
 3183     if (batt->info->batt.get_temp != NULL) {
 
 3184         double T = batt->info->batt.get_temp(batt,
 
 3185             batt->info->userinfo);
 
 3187         mutex_enter(&batt->batt.lock);
 
 3189         mutex_exit(&batt->batt.lock);
 
 3191     temp_coeff = fx_lin_multi2(batt->batt.T, batt_temp_energy_curve,
 
 3192         ARRAY_NUM_ELEM(batt_temp_energy_curve), 
true);
 
 3194     I_max = batt->info->batt.max_pwr / batt->info->batt.volts;
 
 3195     I_rel = batt->batt.prev_amps / I_max;
 
 3197         batt->batt.chg_rel, I_rel);
 
 3199     J_max = batt->info->batt.capacity * temp_coeff;
 
 3200     J = batt->batt.chg_rel * J_max;
 
 3201     J -= U * batt->batt.prev_amps * d_t;
 
 3203     J += batt->batt.rechg_W * d_t;
 
 3204     batt->batt.rechg_W = 0;
 
 3207     if (!batt->rw.failed) {
 
 3208         batt->rw.in_volts = U;
 
 3209         batt->rw.out_volts = U;
 
 3211         batt->rw.in_volts = 0;
 
 3212         batt->rw.out_volts = 0;
 
 3218     batt->batt.chg_rel = clamp(J / J_max, 0, 1);
 
 3222 network_update_cb(elec_comp_t *cb, 
double d_t)
 
 3227     ASSERT(cb->info != NULL);
 
 3228     ASSERT3U(cb->info->type, ==, 
ELEC_CB);
 
 3229     ASSERT3F(cb->info->cb.max_amps, >, 0);
 
 3231     amps_rat = cb->rw.out_amps / cb->info->cb.max_amps;
 
 3233     if (cb->info->cb.triphase)
 
 3235     amps_rat = MIN(amps_rat, 5 * cb->info->cb.rate);
 
 3236     FILTER_IN(cb->scb.temp, amps_rat, d_t, cb->info->cb.rate);
 
 3238     if (cb->scb.temp >= 1.0) {
 
 3239         cb->scb.wk_set = 
false;
 
 3240         cb->scb.cur_set = 
false;
 
 3241 #ifdef  LIBELEC_WITH_LIBSWITCH 
 3243         if (cb->scb.sw != NULL)
 
 3244             libswitch_set(cb->scb.sw, 
true);
 
 3246         if (cb->info->cb.fuse)
 
 3247             cb->rw.failed = 
true;
 
 3252 network_update_tru(elec_comp_t *tru, 
double d_t)
 
 3254     ASSERT(tru != NULL);
 
 3255     ASSERT(tru->info != NULL);
 
 3256     ASSERT3U(tru->info->type, ==, 
ELEC_TRU);
 
 3257     ASSERT3F(d_t, >, 0);
 
 3259     if (tru->ro.in_volts < tru->info->tru.min_volts) {
 
 3263     if (!tru->info->tru.charger) {
 
 3276         ASSERT3F(tru->info->tru.curr_lim, >, 0);
 
 3277         double oc_ratio = tru->tru.prev_amps / tru->info->tru.curr_lim;
 
 3278         double regul_tgt = clamp(oc_ratio > 0 ?
 
 3279             tru->tru.regul / oc_ratio : 1, 0, 1);
 
 3291         } 
else if (regul_tgt > tru->tru.regul) {
 
 3292             FILTER_IN(tru->tru.regul, regul_tgt, d_t, 1);
 
 3294             FILTER_IN(tru->tru.regul, regul_tgt, d_t, 2 * d_t);
 
 3300 network_srcs_update(elec_sys_t *sys, 
double d_t)
 
 3302     ASSERT(sys != NULL);
 
 3303     ASSERT_MUTEX_HELD(&sys->worker_interlock);
 
 3304     ASSERT3F(d_t, >, 0);
 
 3306     for (elec_comp_t *comp = list_head(&sys->comps); comp != NULL;
 
 3307         comp = list_next(&sys->comps, comp)) {
 
 3308         ASSERT(comp->info != NULL);
 
 3309         switch (comp->info->type) {
 
 3311             network_update_batt(comp, d_t);
 
 3314             network_update_gen(comp, d_t);
 
 3317             network_update_tru(comp, d_t);
 
 3326 network_loads_randomize(elec_sys_t *sys, 
double d_t)
 
 3328     ASSERT(sys != NULL);
 
 3329     ASSERT3F(d_t, >, 0);
 
 3331     for (elec_comp_t *comp = list_head(&sys->comps); comp != NULL;
 
 3332         comp = list_next(&sys->comps, comp)) {
 
 3334             FILTER_IN(comp->load.random_load_factor,
 
 3335                 clamp(1.0 + crc64_rand_normal(0.1), 0.8, 1.2),
 
 3342 load_incap_update(elec_comp_t *comp, 
double d_t)
 
 3347     ASSERT(comp != NULL);
 
 3348     ASSERT(comp->info != NULL);
 
 3349     ASSERT3U(comp->info->type, ==, 
ELEC_LOAD);
 
 3350     ASSERT3F(d_t, >, 0);
 
 3353     if (info->load.incap_C == 0)
 
 3356     d_Q = comp->load.incap_d_Q - info->load.incap_leak_Qps * d_t;
 
 3357     comp->load.incap_U += d_Q / info->load.incap_C;
 
 3358     comp->load.incap_U = MAX(comp->load.incap_U, 0);
 
 3359     if (comp->rw.failed)
 
 3360         comp->load.incap_U = 0;
 
 3364 network_loads_update(elec_sys_t *sys, 
double d_t)
 
 3366     ASSERT(sys != NULL);
 
 3367     ASSERT3F(d_t, >, 0);
 
 3369     for (elec_comp_t *comp = list_head(&sys->comps); comp != NULL;
 
 3370         comp = list_next(&sys->comps, comp)) {
 
 3371         ASSERT(comp->info != NULL);
 
 3373         if (comp->info->type == 
ELEC_CB) {
 
 3374             network_update_cb(comp, d_t);
 
 3375         } 
else if (comp->info->type == 
ELEC_LOAD) {
 
 3381             if (!comp->load.seen)
 
 3382                 network_load_integrate_load(NULL, comp, 0, d_t);
 
 3383             load_incap_update(comp, d_t);
 
 3386         comp->rw.in_pwr = comp->rw.in_volts * comp->rw.in_amps;
 
 3387         comp->rw.out_pwr = comp->rw.out_volts * comp->rw.out_amps;
 
 3392 network_ties_update(elec_sys_t *sys)
 
 3394     ASSERT(sys != NULL);
 
 3396     for (elec_comp_t *comp = list_head(&sys->ties); comp != NULL;
 
 3397         comp = list_next(&sys->ties, comp)) {
 
 3398         unsigned n_tied = 0;
 
 3399         int tied[2] = { -1, -1 };
 
 3400         for (
unsigned i = 0; i < comp->n_links; i++) {
 
 3401             if (comp->tie.wk_state[i]) {
 
 3404                 if (n_tied == ARRAY_NUM_ELEM(tied))
 
 3410                 sum_link_amps(&comp->links[tied[0]]),
 
 3411                 sum_link_amps(&comp->links[tied[1]])
 
 3413             comp->rw.out_amps = amps[0] - amps[1];
 
 3414             comp->rw.out_amps = NO_NEG_ZERO(ABS(comp->rw.out_amps));
 
 3415             comp->rw.in_amps = comp->rw.out_amps;
 
 3421 add_src_up(elec_comp_t *comp, elec_comp_t *src, 
const elec_comp_t *upstream)
 
 3423     ASSERT(comp != NULL);
 
 3424     ASSERT(src != NULL);
 
 3425     ASSERT(upstream != NULL);
 
 3427     ASSERT0(src->src_mask & (1 << src->src_idx));
 
 3428     ASSERT3U(comp->n_srcs, <, ELEC_MAX_SRCS);
 
 3429     comp->srcs[comp->n_srcs] = src;
 
 3431     ASSERT3F(src->info->int_R, >, 0);
 
 3432     comp->src_int_cond_total += (1.0 / src->info->int_R) *
 
 3434     for (
unsigned i = 0; i < comp->n_links; i++) {
 
 3435         if (comp->links[i].comp == upstream) {
 
 3436             comp->links[i].srcs[src->src_idx] = src;
 
 3444 network_paint_src_bus(elec_comp_t *src, elec_comp_t *upstream,
 
 3445     elec_comp_t *comp, 
unsigned depth)
 
 3447     ASSERT(src != NULL);
 
 3448     ASSERT(upstream != NULL);
 
 3449     ASSERT(comp != NULL);
 
 3450     ASSERT3U(depth, <, MAX_NETWORK_DEPTH);
 
 3452     if (comp->rw.failed)
 
 3455     add_src_up(comp, src, upstream);
 
 3456     if (comp->rw.in_volts < src->rw.out_volts) {
 
 3457         comp->rw.in_volts = src->rw.out_volts;
 
 3458         comp->rw.in_freq = src->rw.out_freq;
 
 3459         comp->rw.out_volts = comp->rw.in_volts;
 
 3460         comp->rw.out_freq = comp->rw.in_freq;
 
 3462     for (
unsigned i = 0; i < comp->n_links; i++) {
 
 3463         if (comp->links[i].comp != upstream) {
 
 3464             network_paint_src_comp(src, comp,
 
 3465                 comp->links[i].comp, depth + 1);
 
 3471 network_paint_src_tie(elec_comp_t *src, elec_comp_t *upstream,
 
 3472     elec_comp_t *comp, 
unsigned depth)
 
 3474     bool found = 
false, tied = 
false;
 
 3476     ASSERT(src != NULL);
 
 3477     ASSERT(upstream != NULL);
 
 3478     ASSERT(comp != NULL);
 
 3479     ASSERT(comp->info != NULL);
 
 3480     ASSERT3U(comp->info->type, ==, 
ELEC_TIE);
 
 3481     ASSERT3U(depth, <, MAX_NETWORK_DEPTH);
 
 3484     for (
unsigned i = 0; i < comp->n_links; i++) {
 
 3485         if (upstream == comp->links[i].comp) {
 
 3486             if (comp->tie.wk_state[i]) {
 
 3487                 add_src_up(comp, src, upstream);
 
 3488                 if (comp->rw.in_volts < src->rw.out_volts) {
 
 3489                     comp->rw.in_volts = src->rw.out_volts;
 
 3490                     comp->rw.in_freq = src->rw.out_freq;
 
 3500         for (
unsigned i = 0; i < comp->n_links; i++) {
 
 3501             if (upstream != comp->links[i].comp &&
 
 3502                 comp->tie.wk_state[i]) {
 
 3503                 network_paint_src_comp(src, comp,
 
 3504                     comp->links[i].comp, depth + 1);
 
 3511 recalc_out_volts_tru(elec_comp_t *comp)
 
 3513     ASSERT(comp != NULL);
 
 3514     ASSERT3U(comp->info->type, ==, 
ELEC_TRU);
 
 3515     comp->rw.out_volts = comp->tru.regul * comp->info->tru.out_volts *
 
 3516         (comp->rw.in_volts / comp->info->tru.in_volts);
 
 3520 recalc_out_volts_freq_inv(elec_comp_t *comp)
 
 3522     double mult_U, mult_f;
 
 3524     ASSERT(comp != NULL);
 
 3525     ASSERT3U(comp->info->type, ==, 
ELEC_INV);
 
 3527     mult_U = fx_lin(comp->rw.in_volts, comp->info->tru.min_volts,
 
 3528         0.95, comp->info->tru.in_volts, 1);
 
 3529     mult_f = fx_lin(comp->rw.in_volts, comp->info->tru.min_volts,
 
 3530         0.97, comp->info->tru.in_volts, 1);
 
 3531     comp->rw.out_volts = mult_U * comp->info->tru.out_volts;
 
 3532     comp->rw.out_freq = mult_f * comp->info->tru.out_freq;
 
 3536 network_paint_src_tru_inv(elec_comp_t *src, elec_comp_t *upstream,
 
 3537     elec_comp_t *comp, 
unsigned depth)
 
 3539     ASSERT(src != NULL);
 
 3540     ASSERT(upstream != NULL);
 
 3541     ASSERT(comp != NULL);
 
 3542     ASSERT(comp->info != NULL);
 
 3543     ASSERT(comp->info->type == 
ELEC_TRU ||
 
 3545     ASSERT3U(depth, <, MAX_NETWORK_DEPTH);
 
 3548     if (upstream != comp->links[0].comp)
 
 3551     add_src_up(comp, src, upstream);
 
 3552     if (comp->info->type == 
ELEC_TRU) {
 
 3553         ASSERT_MSG(comp->n_srcs == 1, 
"%s attempted to add a second " 
 3554             "AC power source ([0]=%s, [1]=%s). Multi-source feeding " 
 3555             "is NOT supported in AC networks.", comp->info->name,
 
 3556             comp->srcs[0]->info->name, comp->srcs[1]->info->name);
 
 3558     if (!comp->rw.failed) {
 
 3559         if (comp->rw.in_volts < src->rw.out_volts &&
 
 3560             src->rw.out_volts > comp->info->tru.min_volts) {
 
 3561             comp->rw.in_volts = src->rw.out_volts;
 
 3562             comp->rw.in_freq = src->rw.out_freq;
 
 3564                 recalc_out_volts_tru(comp);
 
 3566                 recalc_out_volts_freq_inv(comp);
 
 3569         comp->rw.in_volts = 0;
 
 3570         comp->rw.in_freq = 0;
 
 3571         comp->rw.out_volts = 0;
 
 3572         comp->rw.out_freq = 0;
 
 3574     ASSERT(comp->links[1].comp != NULL);
 
 3578     if (comp->rw.out_volts != 0) {
 
 3579         network_paint_src_comp(comp, comp,
 
 3580             comp->links[1].comp, depth + 1);
 
 3585 network_paint_src_xfrmr(elec_comp_t *src, elec_comp_t *upstream,
 
 3586     elec_comp_t *comp, 
unsigned depth)
 
 3588     ASSERT(src != NULL);
 
 3589     ASSERT(upstream != NULL);
 
 3590     ASSERT(comp != NULL);
 
 3591     ASSERT(comp->info != NULL);
 
 3593     ASSERT3U(depth, <, MAX_NETWORK_DEPTH);
 
 3596     if (upstream != comp->links[0].comp)
 
 3599     add_src_up(comp, src, upstream);
 
 3600     ASSERT_MSG(comp->n_srcs == 1, 
"%s attempted to add a second " 
 3601         "AC power source ([0]=%s, [1]=%s). Multi-source feeding " 
 3602         "is NOT supported in AC networks.", comp->info->name,
 
 3603         comp->srcs[0]->info->name, comp->srcs[1]->info->name);
 
 3605     if (!comp->rw.failed) {
 
 3606         if (comp->rw.in_volts < src->rw.out_volts) {
 
 3607             comp->rw.in_volts = src->rw.out_volts;
 
 3608             comp->rw.out_volts = comp->rw.in_volts *
 
 3609                 (comp->info->xfrmr.out_volts /
 
 3610                 comp->info->xfrmr.in_volts);
 
 3611             comp->rw.in_freq = src->rw.out_freq;
 
 3612             comp->rw.out_freq = comp->rw.in_freq;
 
 3615         comp->rw.in_volts = 0;
 
 3616         comp->rw.out_volts = 0;
 
 3617         comp->rw.in_freq = 0;
 
 3618         comp->rw.out_freq = 0;
 
 3620     ASSERT(comp->links[1].comp != NULL);
 
 3624     if (comp->rw.out_volts != 0) {
 
 3625         network_paint_src_comp(comp, comp,
 
 3626             comp->links[1].comp, depth + 1);
 
 3631 network_paint_src_scb(elec_comp_t *src, elec_comp_t *upstream,
 
 3632     elec_comp_t *comp, 
unsigned depth)
 
 3634     ASSERT(src != NULL);
 
 3635     ASSERT(upstream != NULL);
 
 3636     ASSERT(comp != NULL);
 
 3637     ASSERT3U(depth, <, MAX_NETWORK_DEPTH);
 
 3639     if (!comp->rw.failed && comp->scb.wk_set) {
 
 3640         add_src_up(comp, src, upstream);
 
 3641         if (comp->rw.in_volts < src->rw.out_volts) {
 
 3642             comp->rw.in_volts = src->rw.out_volts;
 
 3643             comp->rw.in_freq = src->rw.out_freq;
 
 3644             comp->rw.out_volts = src->rw.out_volts;
 
 3645             comp->rw.out_freq = src->rw.out_freq;
 
 3647         if (upstream == comp->links[0].comp) {
 
 3648             ASSERT(comp->links[1].comp != NULL);
 
 3649             network_paint_src_comp(src, comp,
 
 3650                 comp->links[1].comp, depth + 1);
 
 3652             ASSERT3P(upstream, ==, comp->links[1].comp);
 
 3653             ASSERT(comp->links[0].comp != NULL);
 
 3654             network_paint_src_comp(src, comp,
 
 3655                 comp->links[0].comp, depth + 1);
 
 3661 network_paint_src_diode(elec_comp_t *src, elec_comp_t *upstream,
 
 3662     elec_comp_t *comp, 
unsigned depth)
 
 3664     ASSERT(src != NULL);
 
 3665     ASSERT(upstream != NULL);
 
 3666     ASSERT(comp != NULL);
 
 3667     ASSERT3U(depth, <, MAX_NETWORK_DEPTH);
 
 3669     if (upstream == comp->links[0].comp) {
 
 3670         add_src_up(comp, src, upstream);
 
 3671         ASSERT0(src->rw.out_freq);
 
 3672         if (!comp->rw.failed) {
 
 3673             if (comp->rw.in_volts < src->rw.out_volts)
 
 3674                 comp->rw.in_volts = src->rw.out_volts;
 
 3676             comp->rw.in_volts = 0;
 
 3678         network_paint_src_comp(src, comp, comp->links[1].comp,
 
 3684 network_paint_src_comp(elec_comp_t *src, elec_comp_t *upstream,
 
 3685     elec_comp_t *comp, 
unsigned depth)
 
 3687     ASSERT(src != NULL);
 
 3688     ASSERT(src->info != NULL);
 
 3689     ASSERT(upstream != NULL);
 
 3690     ASSERT(comp != NULL);
 
 3691     ASSERT3U(depth, <, MAX_NETWORK_DEPTH);
 
 3693     switch (comp->info->type) {
 
 3695         if (src != comp && comp->rw.out_volts < src->rw.out_volts)
 
 3696             add_src_up(comp, src, upstream);
 
 3703             ASSERT(!comp->info->bus.ac);
 
 3705             ASSERT3U(src_is_AC(src->info), ==, comp->info->bus.ac);
 
 3707         network_paint_src_bus(src, upstream, comp, depth);
 
 3711         network_paint_src_tru_inv(src, upstream, comp, depth);
 
 3714         network_paint_src_xfrmr(src, upstream, comp, depth);
 
 3717         add_src_up(comp, src, upstream);
 
 3718         if (!comp->rw.failed) {
 
 3719             if (comp->rw.in_volts < src->rw.out_volts) {
 
 3720                 comp->rw.in_volts = src->rw.out_volts;
 
 3721                 comp->rw.in_freq = src->rw.out_freq;
 
 3724             comp->rw.in_volts = 0;
 
 3725             comp->rw.in_freq = 0;
 
 3730         network_paint_src_scb(src, upstream, comp, depth);
 
 3733         network_paint_src_tie(src, upstream, comp, depth);
 
 3736         network_paint_src_diode(src, upstream, comp, depth);
 
 3744 network_paint(elec_sys_t *sys)
 
 3746     ASSERT(sys != NULL);
 
 3747     ASSERT_MUTEX_HELD(&sys->worker_interlock);
 
 3749     for (elec_comp_t *comp = list_head(&sys->gens_batts); comp != NULL;
 
 3750         comp = list_next(&sys->gens_batts, comp)) {
 
 3751         ASSERT(comp->info != NULL);
 
 3753             comp->info->type == 
ELEC_GEN) && comp->rw.out_volts != 0) {
 
 3754             network_paint_src_comp(comp, comp,
 
 3755                 comp->links[0].comp, 0);
 
 3761 network_load_integrate_tru_inv(
const elec_comp_t *src,
 
 3762     const elec_comp_t *upstream, elec_comp_t *comp, 
unsigned depth, 
double d_t)
 
 3764     ASSERT(src != NULL);
 
 3765     ASSERT(upstream != NULL);
 
 3766     ASSERT(comp != NULL);
 
 3767     ASSERT(comp->links[0].comp != NULL);
 
 3768     ASSERT(comp->links[1].comp != NULL);
 
 3769     ASSERT(comp->info != NULL);
 
 3771     ASSERT3U(depth, <, MAX_NETWORK_DEPTH);
 
 3773     if (upstream != comp->links[0].comp)
 
 3777     comp->rw.out_amps = network_load_integrate_comp(
 
 3778         comp, comp, comp->links[1].comp, depth + 1, d_t);
 
 3779     if (comp->rw.failed || comp->rw.in_volts == 0) {
 
 3780         comp->tru.prev_amps = 0;
 
 3781         comp->rw.in_amps = 0;
 
 3782         comp->rw.out_amps = 0;
 
 3789     comp->tru.prev_amps = comp->rw.out_amps;
 
 3790     comp->tru.eff = fx_lin_multi(comp->rw.out_volts * comp->rw.out_amps,
 
 3791         comp->info->tru.eff_curve, 
true);
 
 3792     ASSERT3F(comp->tru.eff, >, 0);
 
 3793     ASSERT3F(comp->tru.eff, <, 1);
 
 3794     comp->rw.in_amps = ((comp->rw.out_volts / comp->rw.in_volts) *
 
 3795         comp->rw.out_amps) / comp->tru.eff;
 
 3797     return (comp->rw.in_amps);
 
 3801 network_load_integrate_xfrmr(
const elec_comp_t *src,
 
 3802     const elec_comp_t *upstream, elec_comp_t *comp, 
unsigned depth, 
double d_t)
 
 3804     ASSERT(src != NULL);
 
 3805     ASSERT(upstream != NULL);
 
 3806     ASSERT(comp != NULL);
 
 3807     ASSERT(comp->links[0].comp != NULL);
 
 3808     ASSERT(comp->links[1].comp != NULL);
 
 3809     ASSERT(comp->info != NULL);
 
 3811     ASSERT3U(depth, <, MAX_NETWORK_DEPTH);
 
 3813     if (upstream != comp->links[0].comp)
 
 3817     comp->rw.out_amps = network_load_integrate_comp(
 
 3818         comp, comp, comp->links[1].comp, depth + 1, d_t);
 
 3819     if (comp->rw.failed || comp->rw.in_volts == 0) {
 
 3820         comp->rw.in_amps = 0;
 
 3821         comp->rw.out_amps = 0;
 
 3824     comp->xfrmr.eff = fx_lin_multi(comp->rw.out_volts * comp->rw.out_amps,
 
 3825         comp->info->xfrmr.eff_curve, 
true);
 
 3826     ASSERT3F(comp->xfrmr.eff, >, 0);
 
 3827     ASSERT3F(comp->xfrmr.eff, <, 1);
 
 3828     comp->rw.in_amps = ((comp->rw.out_volts / comp->rw.in_volts) *
 
 3829         comp->rw.out_amps) / comp->xfrmr.eff;
 
 3831     return (comp->rw.in_amps);
 
 3835 network_load_integrate_load(
const elec_comp_t *src, elec_comp_t *comp,
 
 3836     unsigned depth, 
double d_t)
 
 3838     double load_WorI, load_I, in_volts_net, incap_I, src_fract;
 
 3842     ASSERT(comp != NULL);
 
 3843     ASSERT(comp->info != NULL);
 
 3844     ASSERT3U(comp->info->type, ==, 
ELEC_LOAD);
 
 3852     in_volts_net = MAX(comp->rw.in_volts, comp->load.incap_U);
 
 3856     if (in_volts_net >= comp->info->load.min_volts) {
 
 3857         load_WorI = info->load.std_load;
 
 3858         if (info->load.get_load != NULL) {
 
 3859             load_WorI += info->load.get_load(comp,
 
 3860                 comp->info->userinfo);
 
 3865     ASSERT3F(load_WorI, >=, 0);
 
 3867     load_WorI *= comp->load.random_load_factor;
 
 3872     if (info->load.stab) {
 
 3873         double volts = MAX(in_volts_net, info->load.min_volts);
 
 3874         ASSERT3F(volts, >, 0);
 
 3875         load_I = load_WorI / volts;
 
 3879     if (comp->rw.shorted) {
 
 3883         ASSERT3F(comp->rw.leak_factor, <, 1);
 
 3884         load_I /= (1 - comp->rw.leak_factor);
 
 3885     } 
else if (comp->rw.failed) {
 
 3895     if (info->load.incap_C > 0 &&
 
 3896         comp->rw.in_volts > comp->load.incap_U + 0.01) {
 
 3917         double U_in = comp->rw.in_volts;
 
 3918         double U_c_old = comp->load.incap_U;
 
 3919         double R = info->load.incap_R;
 
 3920         double C = info->load.incap_C;
 
 3921         double incap_U_new = U_c_old +
 
 3922             ((U_in - U_c_old) * (1 - exp(-d_t / (R * C))));
 
 3939         double Q_old = comp->load.incap_U * info->load.incap_C;
 
 3940         double Q_new = incap_U_new * info->load.incap_C;
 
 3941         double Q_delta = Q_new - Q_old;
 
 3943         incap_I = Q_delta / d_t;
 
 3965     if (comp->load.incap_U > comp->rw.in_volts) {
 
 3967         double load_Q = load_I * d_t;
 
 3969         double avail_Q = (comp->load.incap_U - comp->rw.in_volts) *
 
 3972         double used_Q = MIN(load_Q, avail_Q);
 
 3982         comp->rw.in_amps = load_Q / d_t;
 
 3983         comp->rw.out_amps = load_I;
 
 3984         if (comp->load.incap_U >= comp->info->load.min_volts)
 
 3985             comp->rw.out_volts = comp->load.incap_U;
 
 3987             comp->rw.out_volts = 0;
 
 3988         comp->load.incap_d_Q = -used_Q;
 
 3994         comp->rw.in_amps = load_I + incap_I;
 
 3995         comp->rw.out_amps = load_I;
 
 3996         comp->rw.out_volts = comp->rw.in_volts;
 
 3997         comp->rw.out_freq = comp->rw.in_freq;
 
 3998         comp->load.incap_d_Q = incap_I * d_t;
 
 4000     ASSERT(!isnan(comp->rw.out_amps));
 
 4001     ASSERT(!isnan(comp->rw.out_volts));
 
 4002     comp->load.seen = 
true;
 
 4004         src_fract = get_src_fract(comp, src);
 
 4005         comp->links[0].out_amps[src->src_idx] =
 
 4006             NO_NEG_ZERO(-comp->rw.in_amps * src_fract);
 
 4011     return (comp->rw.in_amps * src_fract);
 
 4015 network_load_integrate_bus(
const elec_comp_t *src, 
const elec_comp_t *upstream,
 
 4016     elec_comp_t *comp, 
unsigned depth, 
double d_t)
 
 4018     double out_amps_total = 0;
 
 4020     ASSERT(src != NULL);
 
 4021     ASSERT(comp != NULL);
 
 4022     ASSERT(comp->info != NULL);
 
 4023     ASSERT3U(comp->info->type, ==, 
ELEC_BUS);
 
 4024     ASSERT3U(depth, <, MAX_NETWORK_DEPTH);
 
 4026     for (
unsigned i = 0; i < comp->n_links; i++) {
 
 4027         ASSERT(comp->links[i].comp != NULL);
 
 4028         if (comp->links[i].comp != upstream) {
 
 4029             comp->links[i].out_amps[src->src_idx] =
 
 4030                 network_load_integrate_comp(src, comp,
 
 4031                 comp->links[i].comp, depth + 1, d_t);
 
 4032             ASSERT3F(comp->links[i].out_amps[src->src_idx], >=, 0);
 
 4033             out_amps_total += comp->links[i].out_amps[src->src_idx];
 
 4036     out_amps_total /= (1 - comp->rw.leak_factor);
 
 4037     return (out_amps_total);
 
 4041 network_load_integrate_tie(
const elec_comp_t *src, 
const elec_comp_t *upstream,
 
 4042     elec_comp_t *comp, 
unsigned depth, 
double d_t)
 
 4044     double out_amps_total = 0;
 
 4046     ASSERT(src != NULL);
 
 4047     ASSERT(comp != NULL);
 
 4048     ASSERT(comp->info != NULL);
 
 4049     ASSERT3U(comp->info->type, ==, 
ELEC_TIE);
 
 4050     ASSERT3U(depth, <, MAX_NETWORK_DEPTH);
 
 4052     for (
unsigned i = 0; i < comp->n_links; i++) {
 
 4053         ASSERT(comp->links[i].comp != NULL);
 
 4054         if (comp->links[i].comp == upstream && !comp->tie.wk_state[i])
 
 4057     for (
unsigned i = 0; i < comp->n_links; i++) {
 
 4058         ASSERT(comp->links[i].comp != NULL);
 
 4059         if (comp->tie.wk_state[i] && comp->links[i].comp != upstream) {
 
 4060             comp->links[i].out_amps[src->src_idx] =
 
 4061                 network_load_integrate_comp(src, comp,
 
 4062                 comp->links[i].comp, depth + 1, d_t);
 
 4063             ASSERT3F(comp->links[i].out_amps[src->src_idx], >=, 0);
 
 4064             out_amps_total += comp->links[i].out_amps[src->src_idx];
 
 4067     return (out_amps_total);
 
 4071 network_load_integrate_scb(
const elec_comp_t *src, 
const elec_comp_t *upstream,
 
 4072     elec_comp_t *comp, 
unsigned depth, 
double d_t)
 
 4074     ASSERT(src != NULL);
 
 4075     ASSERT(upstream != NULL);
 
 4076     ASSERT(comp != NULL);
 
 4077     ASSERT(comp->info != NULL);
 
 4079     ASSERT3U(depth, <, MAX_NETWORK_DEPTH);
 
 4081     if (!comp->scb.wk_set)
 
 4083     for (
unsigned i = 0; i < 2; i++) {
 
 4084         if (comp->links[i].comp == upstream) {
 
 4085             comp->links[!i].out_amps[src->src_idx] =
 
 4086                 network_load_integrate_comp(src, comp,
 
 4087                 comp->links[!i].comp, depth + 1, d_t);
 
 4088             comp->rw.out_amps = sum_link_amps(&comp->links[0]) -
 
 4089                 sum_link_amps(&comp->links[1]);
 
 4090             comp->rw.out_amps = NO_NEG_ZERO(ABS(comp->rw.out_amps));
 
 4091             comp->rw.in_amps = comp->rw.out_amps;
 
 4093             return (comp->links[!i].out_amps[src->src_idx]);
 
 4100 network_load_integrate_batt(
const elec_comp_t *src,
 
 4101     const elec_comp_t *upstream, elec_comp_t *batt, 
unsigned depth, 
double d_t)
 
 4103     ASSERT(src != NULL);
 
 4104     ASSERT(batt != NULL);
 
 4105     ASSERT(batt->info != NULL);
 
 4106     ASSERT3U(batt->info->type, ==, 
ELEC_BATT);
 
 4108     if (depth != 0 && check_upstream(batt, src, upstream)) {
 
 4109         double U_delta = MAX(src->rw.out_volts - batt->rw.out_volts, 0);
 
 4111         ASSERT0(src->rw.out_freq);
 
 4112         if (batt->batt.chg_rel < 1) {
 
 4113             double R = batt->info->batt.chg_R /
 
 4114                 (1 - batt->batt.chg_rel);
 
 4115             batt->rw.in_volts = src->rw.out_volts;
 
 4116             batt->rw.in_amps = U_delta / R;
 
 4122             batt->batt.rechg_W = batt->rw.in_volts *
 
 4125         batt->rw.out_amps = 0;
 
 4127         return (batt->rw.in_amps);
 
 4128     } 
else if (depth == 0) {
 
 4129         batt->rw.out_amps = network_load_integrate_comp(batt, batt,
 
 4130             batt->links[0].comp, depth + 1, d_t);
 
 4131         batt->batt.prev_amps = batt->rw.out_amps;
 
 4133         return (batt->rw.out_amps);
 
 4140 network_load_integrate_gen(elec_comp_t *gen, 
unsigned depth, 
double d_t)
 
 4144     ASSERT(gen != NULL);
 
 4145     ASSERT(gen->info != NULL);
 
 4146     ASSERT3U(gen->info->type, ==, 
ELEC_GEN);
 
 4151     gen->rw.out_amps = network_load_integrate_comp(gen, gen,
 
 4152         gen->links[0].comp, depth + 1, d_t);
 
 4153     gen->rw.in_volts = gen->rw.out_volts;
 
 4154     gen->rw.in_freq = gen->rw.out_freq;
 
 4155     out_pwr = gen->rw.in_volts * gen->rw.out_amps;
 
 4156     gen->gen.eff = fx_lin_multi(out_pwr, gen->info->gen.eff_curve, 
true);
 
 4157     gen->rw.in_amps = gen->rw.out_amps / gen->gen.eff;
 
 4159     return (gen->rw.out_amps);
 
 4163 network_load_integrate_diode(
const elec_comp_t *src,
 
 4164     const elec_comp_t *upstream, elec_comp_t *comp, 
unsigned depth, 
double d_t)
 
 4166     ASSERT(src != NULL);
 
 4167     ASSERT(upstream != NULL);
 
 4168     ASSERT(comp != NULL);
 
 4169     ASSERT3P(upstream, ==, comp->links[0].comp);
 
 4170     ASSERT3F(d_t, >, 0);
 
 4171     ASSERT3U(depth, <, MAX_NETWORK_DEPTH);
 
 4173     comp->links[1].out_amps[src->src_idx] = network_load_integrate_comp(
 
 4174         src, comp, comp->links[1].comp, depth + 1, d_t);
 
 4175     comp->rw.out_amps = sum_link_amps(&comp->links[1]);
 
 4176     comp->rw.in_amps = comp->rw.out_amps;
 
 4177     ASSERT(!isnan(comp->rw.in_amps));
 
 4179     return (comp->links[1].out_amps[src->src_idx]);
 
 4183 network_load_integrate_comp(
const elec_comp_t *src,
 
 4184     const elec_comp_t *upstream, elec_comp_t *comp, 
unsigned depth, 
double d_t)
 
 4186     ASSERT(src != NULL);
 
 4187     ASSERT(src->info != NULL);
 
 4191     ASSERT(comp != NULL);
 
 4192     ASSERT(comp->info != NULL);
 
 4193     ASSERT3U(depth, <, MAX_NETWORK_DEPTH);
 
 4194     ASSERT3F(d_t, >, 0);
 
 4196     if (comp != src && !check_upstream(comp, src, upstream))
 
 4199     switch (comp->info->type) {
 
 4201         return (network_load_integrate_batt(src, upstream, comp,
 
 4204         return (network_load_integrate_gen(comp, depth, d_t));
 
 4207         ASSERT3P(upstream, ==, comp->links[0].comp);
 
 4208         return (network_load_integrate_tru_inv(src, upstream, comp,
 
 4211         ASSERT3P(upstream, ==, comp->links[0].comp);
 
 4212         return (network_load_integrate_xfrmr(src, upstream, comp,
 
 4215         return (network_load_integrate_load(src, comp, depth, d_t));
 
 4217         return (network_load_integrate_bus(src, upstream, comp,
 
 4221         return (network_load_integrate_scb(src, upstream, comp,
 
 4224         return (network_load_integrate_tie(src, upstream, comp,
 
 4227         return (network_load_integrate_diode(src, upstream, comp,
 
 4236 network_load_integrate(elec_sys_t *sys, 
double d_t)
 
 4238     ASSERT(sys != NULL);
 
 4239     ASSERT3F(d_t, >, 0);
 
 4241     for (elec_comp_t *comp = list_head(&sys->gens_batts); comp != NULL;
 
 4242         comp = list_next(&sys->gens_batts, comp)) {
 
 4243         comp->rw.out_amps = network_load_integrate_comp(comp, comp,
 
 4249 network_state_xfer(elec_sys_t *sys)
 
 4251     for (elec_comp_t *comp = list_head(&sys->comps); comp != NULL;
 
 4252         comp = list_next(&sys->comps, comp)) {
 
 4253         mutex_enter(&comp->rw_ro_lock);
 
 4255         if (comp->ro.failed != comp->rw.failed)
 
 4256             comp->rw.failed = comp->ro.failed;
 
 4257         if (comp->ro.shorted != comp->rw.shorted)
 
 4258             comp->rw.shorted = comp->ro.shorted;
 
 4259         comp->ro = comp->rw;
 
 4260         mutex_exit(&comp->rw_ro_lock);
 
 4265 mk_spaces(
char *spaces, 
unsigned len)
 
 4267     memset(spaces, 0, len);
 
 4268     for (
unsigned i = 0; i + 1 < len; i += 2) {
 
 4271             spaces[i + 1] = 
' ';
 
 4273             spaces[i + 1] = 
'-';
 
 4278 print_trace_data(
const elec_comp_t *comp, 
unsigned depth, 
bool out_data,
 
 4284     ASSERT(comp != NULL);
 
 4286     spaces = safe_malloc(2 * depth + 1);
 
 4287     mk_spaces(spaces, 2 * depth + 1);
 
 4288     W = out_data ? (comp->rw.out_volts * comp->rw.out_amps) :
 
 4289         (comp->rw.in_volts * comp->rw.in_amps);
 
 4290     logMsg(
"%s%-5s  %s  %3s: %.2fW  LOADS: %.2fW",
 
 4291         spaces, comp_type2str(comp->info->type), comp->info->name,
 
 4292         out_data ? 
"OUT" : 
"IN", W, load);
 
 4297 network_trace_scb(
const elec_comp_t *upstream, 
const elec_comp_t *comp,
 
 4298     unsigned depth, 
bool do_print)
 
 4300     ASSERT(upstream != NULL);
 
 4301     ASSERT(comp != NULL);
 
 4304     if (!comp->scb.wk_set)
 
 4306     if (upstream == comp->links[0].comp) {
 
 4307         return (network_trace(comp, comp->links[1].comp,
 
 4308             depth + 1, do_print));
 
 4310         return (network_trace(comp, comp->links[0].comp,
 
 4311             depth + 1, do_print));
 
 4316 network_trace(
const elec_comp_t *upstream, 
const elec_comp_t *comp,
 
 4317     unsigned depth, 
bool do_print)
 
 4319     double load_trace = 0;
 
 4321     ASSERT(upstream != NULL);
 
 4322     ASSERT(comp != NULL);
 
 4324     switch (comp->info->type) {
 
 4326         load_trace = network_trace(comp, comp->links[0].comp, depth + 1,
 
 4328         load_trace += comp->rw.out_volts * comp->rw.in_amps;
 
 4330             if (upstream == comp) {
 
 4331                 print_trace_data(comp, depth, 
true, load_trace);
 
 4333                 print_trace_data(comp, depth, 
false,
 
 4336             network_trace(comp, comp->links[0].comp, depth + 1,
 
 4341         load_trace = network_trace(comp, comp->links[0].comp,
 
 4344             print_trace_data(comp, depth, 
true, load_trace);
 
 4345             network_trace(comp, comp->links[0].comp, depth + 1,
 
 4352         if (upstream != comp) {
 
 4354                 print_trace_data(comp, depth, 
false, 0);
 
 4355             return (comp->rw.in_volts * comp->rw.in_amps);
 
 4357             load_trace = network_trace(comp,
 
 4358                 comp->links[0].comp, depth + 1, 
false);
 
 4360                 print_trace_data(comp, depth, 
true, load_trace);
 
 4361                 network_trace(comp, comp->links[0].comp,
 
 4368             print_trace_data(comp, depth, 
false, 0);
 
 4369         return (comp->rw.in_volts * comp->rw.in_amps);
 
 4371         for (
unsigned i = 0; i < comp->n_links; i++) {
 
 4372             load_trace += network_trace(comp, comp->links[i].comp,
 
 4376             print_trace_data(comp, depth, 
false, load_trace);
 
 4377             for (
unsigned i = 0; i < comp->n_links; i++) {
 
 4378                 network_trace(comp, comp->links[i].comp,
 
 4385         load_trace = network_trace_scb(upstream, comp, depth, 
false);
 
 4387             print_trace_data(comp, depth, 
false, load_trace);
 
 4388             network_trace_scb(upstream, comp, depth, 
true);
 
 4392         for (
unsigned i = 0; i < comp->n_links; i++) {
 
 4393             if (comp->tie.wk_state[i]) {
 
 4394                 load_trace += network_trace(comp,
 
 4395                     comp->links[i].comp, depth + 1, 
false);
 
 4399             print_trace_data(comp, depth, 
false, load_trace);
 
 4400             for (
unsigned i = 0; i < comp->n_links; i++) {
 
 4401                 if (comp->tie.wk_state[i]) {
 
 4402                     load_trace += network_trace(comp,
 
 4403                         comp->links[i].comp, depth + 1,
 
 4410         load_trace = network_trace(comp, comp->links[1].comp,
 
 4413             print_trace_data(comp, depth, 
false, load_trace);
 
 4414             network_trace(comp, comp->links[1].comp,
 
 4422     return (load_trace);
 
 4426 elec_sys_worker(
void *userinfo)
 
 4429     uint64_t now = microclock();
 
 4431 #ifdef  LIBELEC_TIMING_DEBUG 
 4432     static int steps = 0;
 
 4433     static double d_t_total = 0;
 
 4434     static uint64_t last_report = 0;
 
 4437     ASSERT(userinfo != NULL);
 
 4440     mutex_enter(&sys->paused_lock);
 
 4441     if (sys->paused || sys->prev_clock == 0) {
 
 4442         mutex_exit(&sys->paused_lock);
 
 4443         sys->prev_clock = now;
 
 4446     d_t = USEC2SEC(now - sys->prev_clock) * sys->time_factor;
 
 4447     sys->prev_clock = now;
 
 4448     mutex_exit(&sys->paused_lock);
 
 4450 #ifdef  LIBELEC_TIMING_DEBUG 
 4453     if (now - last_report > SEC2USEC(1)) {
 
 4454         logMsg(
"steps: %4d  d_t_avg: %f", steps, d_t_total / steps);
 
 4465 #ifdef  LIBELEC_WITH_NETLINK 
 4466     if (sys->net_recv.active) {
 
 4467         elec_net_recv_update(sys);
 
 4471     mutex_enter(&sys->worker_interlock);
 
 4473     mutex_enter(&sys->user_cbs_lock);
 
 4474     for (
user_cb_info_t *ucbi = avl_first(&sys->user_cbs); ucbi != NULL;
 
 4475         ucbi = AVL_NEXT(&sys->user_cbs, ucbi)) {
 
 4477             ASSERT(ucbi->cb != NULL);
 
 4478             ucbi->cb(sys, 
true, ucbi->userinfo);
 
 4481     mutex_exit(&sys->user_cbs_lock);
 
 4483     network_reset(sys, d_t);
 
 4484     network_srcs_update(sys, d_t);
 
 4485     network_loads_randomize(sys, d_t);
 
 4487     network_load_integrate(sys, d_t);
 
 4488     network_loads_update(sys, d_t);
 
 4489     network_ties_update(sys);
 
 4494     network_state_xfer(sys);
 
 4496     mutex_enter(&sys->user_cbs_lock);
 
 4497     for (
user_cb_info_t *ucbi = avl_first(&sys->user_cbs); ucbi != NULL;
 
 4498         ucbi = AVL_NEXT(&sys->user_cbs, ucbi)) {
 
 4500             ASSERT(ucbi->cb != NULL);
 
 4501             ucbi->cb(sys, 
false, ucbi->userinfo);
 
 4504     mutex_exit(&sys->user_cbs_lock);
 
 4506     mutex_exit(&sys->worker_interlock);
 
 4508 #ifdef  LIBELEC_WITH_NETLINK 
 4509     elec_net_send_update(sys);
 
 4515 comp_free(elec_comp_t *comp)
 
 4517     ASSERT(comp != NULL);
 
 4520 #ifdef  LIBELEC_WITH_DRS 
 4521     dr_delete(&comp->drs.in_volts);
 
 4522     dr_delete(&comp->drs.out_volts);
 
 4523     dr_delete(&comp->drs.in_amps);
 
 4524     dr_delete(&comp->drs.out_amps);
 
 4525     dr_delete(&comp->drs.in_pwr);
 
 4526     dr_delete(&comp->drs.out_pwr);
 
 4530         mutex_destroy(&comp->batt.lock);
 
 4531     else if (comp->info->type == 
ELEC_GEN)
 
 4532         mutex_destroy(&comp->gen.lock);
 
 4534     ZERO_FREE_N(comp->links, comp->n_links);
 
 4535     if (comp->info->type == 
ELEC_TIE) {
 
 4536         free(comp->tie.cur_state);
 
 4537         free(comp->tie.wk_state);
 
 4538         mutex_destroy(&comp->tie.lock);
 
 4540     mutex_destroy(&comp->rw_ro_lock);
 
 4542     memset(comp, 0, 
sizeof (*comp));
 
 4553     ASSERT(comp != NULL);
 
 4554     ASSERT(comp->info != NULL);
 
 4555     ASSERT3U(comp->info->type, ==, 
ELEC_CB);
 
 4560     comp->scb.cur_set = set;
 
 4571     ASSERT(comp != NULL);
 
 4572     ASSERT(comp->info != NULL);
 
 4573     ASSERT3U(comp->info->type, ==, 
ELEC_CB);
 
 4575     return (comp->scb.cur_set);
 
 4586     ASSERT(comp != NULL);
 
 4587     ASSERT(comp->info != NULL);
 
 4588     ASSERT3U(comp->info->type, ==, 
ELEC_CB);
 
 4590     return (comp->scb.temp);
 
 4606     elec_comp_t *
const bus_list[STATIC_ARRAY_LEN_ARG(list_len)])
 
 4610     ASSERT(comp != NULL);
 
 4611     ASSERT(comp->info != NULL);
 
 4612     ASSERT3U(comp->info->type, ==, 
ELEC_TIE);
 
 4613     ASSERT(bus_list != NULL || list_len == 0);
 
 4616     if (comp->ro.failed)
 
 4619     if (list_len == 0) {
 
 4620         mutex_enter(&comp->tie.lock);
 
 4621         memset(comp->tie.cur_state, 0,
 
 4622             comp->n_links * sizeof (*comp->tie.cur_state));
 
 4623         mutex_exit(&comp->tie.lock);
 
 4628     new_state = safe_calloc(comp->n_links, sizeof (*new_state));
 
 4629     for (
size_t i = 0; i < list_len; i++) {
 
 4631         for (j = 0; j < comp->n_links; j++) {
 
 4632             if (comp->links[j].comp == bus_list[i]) {
 
 4633                 new_state[j] = 
true;
 
 4637         ASSERT_MSG(j != comp->n_links,
 
 4638             "Tie %s is not connected to bus %s", comp->info->name,
 
 4639             bus_list[i]->info->name);
 
 4642     mutex_enter(&comp->tie.lock);
 
 4643     memcpy(comp->tie.cur_state, new_state,
 
 4644         comp->n_links * sizeof (*comp->tie.cur_state));
 
 4645     mutex_exit(&comp->tie.lock);
 
 4663     ASSERT(comp != NULL);
 
 4679     elec_comp_t **bus_list;
 
 4682     ASSERT(comp != NULL);
 
 4683     ASSERT(comp->sys != NULL);
 
 4684     ASSERT(comp->info != NULL);
 
 4685     ASSERT3U(comp->info->type, ==, 
ELEC_TIE);
 
 4687     if (comp->ro.failed)
 
 4691     for (len = 0; va_arg(ap2, elec_comp_t *) != NULL; len++)
 
 4695     bus_list = safe_malloc(
sizeof (*bus_list) * len);
 
 4696     for (
size_t i = 0; i < len; i++)
 
 4697         bus_list[i] = va_arg(ap, elec_comp_t *);
 
 4715     ASSERT(comp != NULL);
 
 4716     ASSERT(comp->info != NULL);
 
 4717     ASSERT3U(comp->info->type, ==, 
ELEC_TIE);
 
 4719     if (comp->ro.failed)
 
 4722     mutex_enter(&comp->tie.lock);
 
 4723     for (
unsigned i = 0; i < comp->n_links; i++)
 
 4724         comp->tie.cur_state[i] = tied;
 
 4725     mutex_exit(&comp->tie.lock);
 
 4742     ASSERT(comp != NULL);
 
 4743     ASSERT(comp->info != NULL);
 
 4744     ASSERT3U(comp->info->type, ==, 
ELEC_TIE);
 
 4746     mutex_enter(&comp->tie.lock);
 
 4747     for (
unsigned i = 0; tied && i < comp->n_links; i++)
 
 4748         tied &= comp->tie.cur_state[i];
 
 4749     mutex_exit(&comp->tie.lock);
 
 4769     elec_comp_t *bus_list[STATIC_ARRAY_LEN_ARG(cap)])
 
 4773     ASSERT(comp != NULL);
 
 4774     ASSERT(comp->info != NULL);
 
 4775     ASSERT3U(comp->info->type, ==, 
ELEC_TIE);
 
 4776     ASSERT3U(cap, >=, comp->n_links);
 
 4777     ASSERT(bus_list != NULL);
 
 4779     mutex_enter(&comp->tie.lock);
 
 4780     for (
unsigned i = 0; i < comp->n_links; i++) {
 
 4781         if (comp->tie.cur_state[i])
 
 4782             bus_list[n_tied++] = comp->links[i].comp;
 
 4784     mutex_exit(&comp->tie.lock);
 
 4801     ASSERT(comp != NULL);
 
 4802     ASSERT(comp->info != NULL);
 
 4803     ASSERT3U(comp->info->type, ==, 
ELEC_TIE);
 
 4805     return (comp->n_links);
 
 4830     ASSERT(tie != NULL);
 
 4831     ASSERT3U(tie->info->type, ==, 
ELEC_TIE);
 
 4832     va_start(ap, exhaustive);
 
 4847     size_t list_len = 0, n_tied = 0;
 
 4848     const elec_comp_t *bus;
 
 4850     ASSERT(tie != NULL);
 
 4851     ASSERT3U(tie->info->type, ==, 
ELEC_TIE);
 
 4853     mutex_enter(&tie->tie.lock);
 
 4854     for (
unsigned i = 0; i < tie->n_links; i++) {
 
 4855         if (tie->tie.cur_state[i])
 
 4858     for (list_len = 0; (bus = va_arg(ap, 
const elec_comp_t *)) != NULL;
 
 4861         for (
unsigned i = 0; i < tie->n_links; i++) {
 
 4862             if (tie->links[i].comp == bus) {
 
 4864                 if (!tie->tie.cur_state[i]) {
 
 4873     if (list_len != n_tied && exclusive)
 
 4876     mutex_exit(&tie->tie.lock);
 
 4895     ASSERT(gen != NULL);
 
 4896     ASSERT(gen->info != NULL);
 
 4897     ASSERT3U(gen->info->type, ==, 
ELEC_GEN);
 
 4898     ASSERT_MSG(gen->info->gen.get_rpm == NULL,
 
 4899         "Attempted to call libelec_gen_set_rpm() for generator %s, " 
 4900         "however this generator already had an rpm callback set using " 
 4901         "libelec_gen_set_rpm_cb(). You may NOT mix both mechanisms for " 
 4902         "setting a generator's speed. Either set the speed directly " 
 4903         "using libelec_gen_set_rpm() -OR- use the callback method " 
 4904         "using libelec_gen_set_rpm_cb(), but not both.", gen->info->name);
 
 4905     mutex_enter(&gen->gen.lock);
 
 4907     mutex_exit(&gen->gen.lock);
 
 4923     ASSERT(gen != NULL);
 
 4924     ASSERT(gen->info != NULL);
 
 4925     ASSERT3U(gen->info->type, ==, 
ELEC_GEN);
 
 4926     mutex_enter(&((elec_comp_t *)gen)->gen.lock);
 
 4928     mutex_exit(&((elec_comp_t *)gen)->gen.lock);
 
 4940     ASSERT(batt != NULL);
 
 4941     ASSERT(batt->info != NULL);
 
 4942     ASSERT3U(batt->info->type, ==, 
ELEC_BATT);
 
 4943     return (batt->batt.chg_rel);
 
 4955     ASSERT(batt != NULL);
 
 4956     ASSERT(batt->info != NULL);
 
 4957     ASSERT3U(batt->info->type, ==, 
ELEC_BATT);
 
 4958     ASSERT3F(chg_rel, >=, 0);
 
 4959     ASSERT3F(chg_rel, <=, 1);
 
 4961     mutex_enter(&batt->sys->worker_interlock);
 
 4962     batt->batt.chg_rel = chg_rel;
 
 4964     batt->batt.rechg_W = 0;
 
 4965     mutex_exit(&batt->sys->worker_interlock);
 
 4977     ASSERT(batt != NULL);
 
 4978     ASSERT(batt->info != NULL);
 
 4979     ASSERT3U(batt->info->type, ==, 
ELEC_BATT);
 
 4980     mutex_enter(&((elec_comp_t *)batt)->batt.lock);
 
 4982     mutex_exit(&((elec_comp_t *)batt)->batt.lock);
 
 4996     ASSERT(batt != NULL);
 
 4997     ASSERT(batt->info != NULL);
 
 4998     ASSERT3U(batt->info->type, ==, 
ELEC_BATT);
 
 5000     mutex_enter(&batt->batt.lock);
 
 5002     mutex_exit(&batt->batt.lock);
 
 5011     ASSERT(chgr != NULL);
 
 5012     ASSERT(chgr->info != NULL);
 
 5013     ASSERT3U(chgr->info->type, ==, 
ELEC_TRU);
 
 5014     ASSERT(chgr->info->tru.charger);
 
 5015     ASSERT(chgr->tru.batt_conn != NULL);
 
 5016     return (chgr->ro.in_volts > 90 &&
 
 5035     static const vect2_t chg_volt_curve[] = {
 
 5047     ASSERT3F(U_nominal, >, 0);
 
 5048     ASSERT3F(chg_rel, >=, 0);
 
 5053     ASSERT3F(chg_rel, <=, 1.0001);
 
 5054     I_rel = clamp(I_rel, 0, 1);
 
 5055     return (U_nominal * (1 - clamp(pow(I_rel, 1.45), 0, 1)) *
 
 5056         fx_lin_multi2(chg_rel, chg_volt_curve,
 
 5057         ARRAY_NUM_ELEM(chg_volt_curve), 
true));
 
 5060 #ifdef  LIBELEC_WITH_NETLINK 
 5065     ASSERT(sys != NULL);
 
 5066     ASSERT_MUTEX_HELD(&sys->worker_interlock);
 
 5067     ASSERT(conn != NULL);
 
 5069     list_remove(&sys->net_send.conns_list, conn);
 
 5070     htbl_remove(&sys->net_send.conns, &conn->conn_id, 
false);
 
 5077 conn_add_notif(netlink_conn_id_t conn_id, netlink_conn_ev_t ev, 
void *userinfo)
 
 5083     ASSERT(userinfo != NULL);
 
 5086     mutex_enter(&sys->worker_interlock);
 
 5087     if (sys->net_recv.active && sys->started)
 
 5088         send_net_recv_map(sys);
 
 5089     mutex_exit(&sys->worker_interlock);
 
 5093 conn_rem_notif(netlink_conn_id_t conn_id, netlink_conn_ev_t ev,
 
 5100     ASSERT(userinfo != NULL);
 
 5103     mutex_enter(&sys->worker_interlock);
 
 5104     conn = htbl_lookup(&sys->net_send.conns, &conn_id);
 
 5106         kill_conn(sys, conn);
 
 5107     mutex_exit(&sys->worker_interlock);
 
 5111 libelec_enable_net_send(elec_sys_t *sys)
 
 5113     ASSERT(sys != NULL);
 
 5114     ASSERT(!sys->started);
 
 5115     ASSERT(!sys->net_send.active);
 
 5116     ASSERT(!sys->net_recv.active);
 
 5118     sys->net_send.active = 
true;
 
 5119     sys->net_send.xmit_ctr = 0;
 
 5120     htbl_create(&sys->net_send.conns, 128, sizeof (netlink_conn_id_t),
 
 5122     list_create(&sys->net_send.conns_list, sizeof (
net_conn_t),
 
 5125     sys->net_send.proto.proto_id = NETLINK_PROTO_LIBELEC;
 
 5126     sys->net_send.proto.name = 
"libelec";
 
 5127     sys->net_send.proto.msg_rcvd_notif = netlink_send_msg_notif;
 
 5128     sys->net_send.proto.conn_rem_notif = conn_rem_notif;
 
 5129     sys->net_send.proto.userinfo = sys;
 
 5130     netlink_add_proto(&sys->net_send.proto);
 
 5134 net_conn_free(
void *net_conn, 
void *unused)
 
 5138     ASSERT(net_conn != NULL);
 
 5146 libelec_disable_net_send(elec_sys_t *sys)
 
 5148     ASSERT(sys != NULL);
 
 5149     ASSERT(!sys->started);
 
 5151     if (sys->net_send.active) {
 
 5152         netlink_remove_proto(&sys->net_send.proto);
 
 5153         while (list_remove_head(&sys->net_send.conns_list) != NULL)
 
 5155         list_destroy(&sys->net_send.conns_list);
 
 5156         htbl_empty(&sys->net_send.conns, net_conn_free, NULL);
 
 5157         htbl_destroy(&sys->net_send.conns);
 
 5158         sys->net_send.active = 
false;
 
 5163 libelec_enable_net_recv(elec_sys_t *sys)
 
 5165     ASSERT(sys != NULL);
 
 5166     ASSERT(!sys->started);
 
 5167     ASSERT(!sys->net_send.active);
 
 5168     ASSERT(!sys->net_recv.active);
 
 5170     sys->net_recv.map = safe_calloc(NETMAPSZ(sys),
 
 5171         sizeof (*sys->net_recv.map));
 
 5172     sys->net_recv.map_dirty = 
false;
 
 5173     sys->net_recv.active = 
true;
 
 5175     sys->net_recv.proto.proto_id = NETLINK_PROTO_LIBELEC;
 
 5176     sys->net_recv.proto.name = 
"libelec";
 
 5177     sys->net_recv.proto.msg_rcvd_notif = netlink_recv_msg_notif;
 
 5178     sys->net_recv.proto.conn_add_notif = conn_add_notif;
 
 5179     sys->net_recv.proto.userinfo = sys;
 
 5181     netlink_add_proto(&sys->net_recv.proto);
 
 5185 libelec_disable_net_recv(elec_sys_t *sys)
 
 5187     ASSERT(sys != NULL);
 
 5188     ASSERT(!sys->started);
 
 5189     if (sys->net_recv.active) {
 
 5190         netlink_remove_proto(&sys->net_recv.proto);
 
 5191         free(sys->net_recv.map);
 
 5192         sys->net_recv.map = NULL;
 
 5193         sys->net_recv.active = 
false;
 
 5198 get_net_conn(elec_sys_t *sys, netlink_conn_id_t conn_id)
 
 5202     ASSERT(sys != NULL);
 
 5203     ASSERT_MUTEX_HELD(&sys->worker_interlock);
 
 5205     conn = htbl_lookup(&sys->net_send.conns, &conn_id);
 
 5207         conn = safe_calloc(1, 
sizeof (*conn));
 
 5208         conn->conn_id = conn_id;
 
 5209         conn->map = safe_calloc(NETMAPSZ(sys), 
sizeof (*conn->map));
 
 5210         delay_line_init(&conn->kill_delay, SEC2USEC(20));
 
 5211         htbl_set(&sys->net_send.conns, &conn_id, conn);
 
 5212         list_insert_tail(&sys->net_send.conns_list, conn);
 
 5221     ASSERT(sys != NULL);
 
 5222     ASSERT(conn != NULL);
 
 5223     ASSERT(req != NULL);
 
 5225     memcpy(conn->map, req->map, NETMAPSZ(sys));
 
 5226     conn->num_active = 0;
 
 5227     for (
unsigned i = 0, n = list_count(&sys->comps); i < n; i++) {
 
 5228         if (NETMAPGET(conn->map, i))
 
 5231     DELAY_LINE_PUSH_IMM(&conn->kill_delay, 
false);
 
 5235 netlink_send_msg_notif(netlink_conn_id_t conn_id, 
const void *buf, 
size_t sz,
 
 5242     ASSERT(buf != NULL);
 
 5243     ASSERT(userinfo != NULL);
 
 5247         logMsg(
"Received bad packet length %d", (
int)sz);
 
 5251     if (req->version != LIBELEC_NET_VERSION) {
 
 5252         logMsg(
"Received bad version %d which doesn't match ours (%d)",
 
 5253             req->version, LIBELEC_NET_VERSION);
 
 5256     mutex_enter(&sys->worker_interlock);
 
 5257     conn = get_net_conn(sys, conn_id);
 
 5258     if (req->req == NET_REQ_MAP) {
 
 5261         if (map->conf_crc == sys->conf_crc &&
 
 5263             handle_net_req_map(sys, conn, buf);
 
 5266             logMsg(
"Cannot handle net map req, elec file " 
 5267                 "CRC mismatch (ours: %llx theirs: %llx)",
 
 5268                 (
unsigned long long)sys->conf_crc,
 
 5269                 (
unsigned long long)map->conf_crc);
 
 5271             logMsg(
"Cannot handle net map req, elec file " 
 5276         logMsg(
"Unknown or malformed req %x of length %d",
 
 5279     mutex_exit(&sys->worker_interlock);
 
 5283 send_xmit_data_conn(elec_sys_t *sys, 
net_conn_t *conn)
 
 5288     ASSERT(sys != NULL);
 
 5289     ASSERT_MUTEX_HELD(&sys->worker_interlock);
 
 5290     ASSERT(conn != NULL);
 
 5294     rep = safe_calloc(1, repsz);
 
 5295     rep->version = LIBELEC_NET_VERSION;
 
 5296     rep->rep = NET_REP_COMPS;
 
 5297     rep->conf_crc = sys->conf_crc;
 
 5298     for (
unsigned i = 0, n = list_count(&sys->comps); i < n; i++) {
 
 5299         const elec_comp_t *comp = sys->comps_array[i];
 
 5301         if (NETMAPGET(conn->map, i)) {
 
 5303             ASSERT3U(rep->n_comps, <=, conn->num_active);
 
 5307             if (comp->ro.failed)
 
 5308                 data->flags |= LIBELEC_NET_FLAG_FAILED;
 
 5309             if (comp->ro.shorted)
 
 5310                 data->flags |= LIBELEC_NET_FLAG_SHORTED;
 
 5311             data->in_volts = clampi(round(comp->ro.in_volts *
 
 5312                 NET_VOLTS_FACTOR), 0, UINT16_MAX);
 
 5313             data->out_volts = clampi(round(comp->ro.out_volts *
 
 5314                 NET_VOLTS_FACTOR), 0, UINT16_MAX);
 
 5315             data->in_amps = clampi(round(comp->ro.in_amps *
 
 5316                 NET_AMPS_FACTOR), 0, UINT16_MAX);
 
 5317             data->out_amps = clampi(round(comp->ro.out_amps *
 
 5318                 NET_AMPS_FACTOR), 0, UINT16_MAX);
 
 5319             data->in_freq = clampi(round(comp->ro.in_freq *
 
 5320                 NET_FREQ_FACTOR), 0, UINT16_MAX);
 
 5321             data->out_freq = clampi(round(comp->ro.out_freq *
 
 5322                 NET_FREQ_FACTOR), 0, UINT16_MAX);
 
 5323             data->leak_factor = round(comp->ro.leak_factor * 10000);
 
 5326     ASSERT3U(rep->n_comps, ==, conn->num_active);
 
 5327     (void)netlink_sendto(NETLINK_PROTO_LIBELEC, rep, repsz,
 
 5332 elec_net_send_update(elec_sys_t *sys)
 
 5334     ASSERT(sys != NULL);
 
 5336     sys->net_send.xmit_ctr = (sys->net_send.xmit_ctr + 1) % NET_XMIT_INTVAL;
 
 5337     if (sys->net_send.xmit_ctr != 0)
 
 5343     mutex_enter(&sys->worker_interlock);
 
 5344     for (
net_conn_t *conn = list_head(&sys->net_send.conns_list),
 
 5345         *next_conn = NULL; conn != NULL; conn = next_conn) {
 
 5346         next_conn = list_next(&sys->net_send.conns_list, conn);
 
 5347         send_xmit_data_conn(sys, conn);
 
 5349     mutex_exit(&sys->worker_interlock);
 
 5355     ASSERT(sys != NULL);
 
 5356     ASSERT(comps != NULL);
 
 5358     NET_DBG_LOG(
"New dev data with %d comps", (
int)comps->n_comps);
 
 5360     for (
unsigned i = 0; i < comps->n_comps; i++) {
 
 5364         if (data->idx >= list_count(&sys->comps))
 
 5366         comp = sys->comps_array[data->idx];
 
 5368         mutex_enter(&comp->rw_ro_lock);
 
 5370         comp->rw.in_volts = (data->in_volts / NET_VOLTS_FACTOR);
 
 5371         comp->rw.out_volts = (data->out_volts / NET_VOLTS_FACTOR);
 
 5372         comp->rw.in_amps = (data->in_amps / NET_AMPS_FACTOR);
 
 5373         comp->rw.out_amps = (data->out_amps / NET_AMPS_FACTOR);
 
 5374         comp->rw.in_pwr = comp->rw.in_volts * comp->rw.in_amps;
 
 5375         comp->rw.out_pwr = comp->rw.out_volts * comp->rw.out_amps;
 
 5376         comp->rw.in_freq = (data->in_freq / NET_FREQ_FACTOR);
 
 5377         comp->rw.out_freq = (data->out_freq / NET_FREQ_FACTOR);
 
 5378         comp->rw.leak_factor = (data->leak_factor / 10000.0);
 
 5379         comp->rw.failed = !!(data->flags & LIBELEC_NET_FLAG_FAILED);
 
 5380         comp->rw.shorted = !!(data->flags & LIBELEC_NET_FLAG_SHORTED);
 
 5382         comp->ro = comp->rw;
 
 5384         mutex_exit(&comp->rw_ro_lock);
 
 5389 netlink_recv_msg_notif(netlink_conn_id_t conn_id, 
const void *buf, 
size_t sz,
 
 5397     ASSERT(buf != NULL);
 
 5400     ASSERT(userinfo != NULL);
 
 5404         logMsg(
"Received bad packet length %d", (
int)sz);
 
 5407     if (rep->version != LIBELEC_NET_VERSION) {
 
 5408         logMsg(
"Received bad version %d which doesn't " 
 5409             "match ours (%d)", rep->version, LIBELEC_NET_VERSION);
 
 5412     if (rep->rep == NET_REP_COMPS &&
 
 5416         if (rep_comps->conf_crc == sys->conf_crc) {
 
 5417             handle_net_rep_comps(sys, rep_comps);
 
 5420             logMsg(
"Cannot handle rep COMPS, elec file " 
 5421                 "CRC mismatch (ours: %llx theirs: %llx)",
 
 5422                 (
unsigned long long)sys->conf_crc,
 
 5423                 (
unsigned long long)rep_comps->conf_crc);
 
 5425             logMsg(
"Cannot handle rep COMPS, elec file " 
 5430         logMsg(
"Unknown or malformed rep %x of length %d",
 
 5436 elec_net_recv_update(elec_sys_t *sys)
 
 5438     ASSERT(sys != NULL);
 
 5439     if (netlink_started() && sys->net_recv.map_dirty) {
 
 5440         mutex_enter(&sys->worker_interlock);
 
 5441         if (send_net_recv_map(sys))
 
 5442             sys->net_recv.map_dirty = 
false;
 
 5443         mutex_exit(&sys->worker_interlock);
 
 5448 send_net_recv_map(elec_sys_t *sys)
 
 5453     ASSERT(sys != NULL);
 
 5454     ASSERT(sys->started);
 
 5455     ASSERT(sys->net_recv.active);
 
 5456     req = safe_calloc(1, NETMAPSZ_REQ(sys));
 
 5457     req->version = LIBELEC_NET_VERSION;
 
 5458     req->req = NET_REQ_MAP;
 
 5459     req->conf_crc = sys->conf_crc;
 
 5460     memcpy(req->map, sys->net_recv.map, NETMAPSZ(sys));
 
 5461     res = netlink_send(NETLINK_PROTO_LIBELEC, req, NETMAPSZ_REQ(sys), 0);
 
 5468 net_add_recv_comp(elec_comp_t *comp)
 
 5472     ASSERT(comp != NULL);
 
 5474     if (!sys->net_recv.active)
 
 5476     ASSERT3U(comp->comp_idx, <, list_count(&sys->comps));
 
 5477     if (!NETMAPGET(sys->net_recv.map, comp->comp_idx)) {
 
 5478         mutex_enter(&sys->worker_interlock);
 
 5479         if (!NETMAPGET(sys->net_recv.map, comp->comp_idx)) {
 
 5480             NETMAPSET(sys->net_recv.map, comp->comp_idx);
 
 5481             sys->net_recv.map_dirty = 
true;
 
 5483         mutex_exit(&sys->worker_interlock);
 
double libelec_batt_get_chg_rel(const elec_comp_t *batt)
bool libelec_comp_is_AC(const elec_comp_t *comp)
double libelec_gen_set_random_freq(elec_comp_t *comp, double stddev)
void libelec_comp_set_userinfo(elec_comp_t *comp, void *userinfo)
double libelec_comp_get_out_amps(const elec_comp_t *comp)
void libelec_gen_set_rpm_cb(elec_comp_t *gen, elec_get_rpm_cb_t cb)
void libelec_sys_set_time_factor(elec_sys_t *sys, double time_factor)
elec_get_rpm_cb_t libelec_gen_get_rpm_cb(const elec_comp_t *gen)
double(* elec_get_load_cb_t)(elec_comp_t *comp, void *userinfo)
elec_comp_t * libelec_comp_find(elec_sys_t *sys, const char *name)
elec_get_load_cb_t libelec_load_get_load_cb(elec_comp_t *load)
double libelec_comp_get_out_freq(const elec_comp_t *comp)
void libelec_tie_set(elec_comp_t *comp,...) SENTINEL_ATTR
double libelec_phys_get_batt_voltage(double U_nominal, double chg_rel, double I_rel)
double(* elec_get_temp_cb_t)(elec_comp_t *comp, void *userinfo)
double libelec_comp_get_eff(const elec_comp_t *gen)
void libelec_walk_comps(const elec_sys_t *sys, void(*cb)(elec_comp_t *, void *), void *userinfo)
const elec_comp_info_t * libelec_get_comp_infos(const elec_sys_t *sys, size_t *num_infos)
bool libelec_cb_get(const elec_comp_t *comp)
void libelec_sys_stop(elec_sys_t *sys)
bool libelec_comp_get_failed(const elec_comp_t *comp)
double libelec_comp_get_out_pwr(const elec_comp_t *comp)
const elec_comp_info_t * libelec_comp2info(const elec_comp_t *comp)
bool libelec_comp_get_shorted(const elec_comp_t *comp)
double libelec_batt_get_temp(const elec_comp_t *batt)
void libelec_batt_set_temp_cb(elec_comp_t *batt, elec_get_temp_cb_t cb)
double libelec_comp_get_in_freq(const elec_comp_t *comp)
bool libelec_deserialize(elec_sys_t *sys, const conf_t *ser, const char *prefix)
void libelec_load_set_load_cb(elec_comp_t *load, elec_get_load_cb_t cb)
bool libelec_comp_is_powered(const elec_comp_t *comp)
elec_sys_t * libelec_new(const char *filename)
double libelec_comp_get_in_volts(const elec_comp_t *comp)
bool libelec_tie_get(elec_comp_t *tie, bool_t exhaustive,...) SENTINEL_ATTR
double libelec_comp_get_in_pwr(const elec_comp_t *comp)
void libelec_batt_set_temp(elec_comp_t *batt, double T)
size_t libelec_tie_get_num_buses(const elec_comp_t *comp)
void libelec_comp_set_shorted(elec_comp_t *comp, bool shorted)
void libelec_add_user_cb(elec_sys_t *sys, bool pre, elec_user_cb_t cb, void *userinfo)
void * libelec_comp_get_userinfo(const elec_comp_t *comp)
double libelec_gen_get_rpm(const elec_comp_t *gen)
void libelec_tie_set_list(elec_comp_t *comp, size_t list_len, elec_comp_t *const bus_list[])
double libelec_comp_get_in_amps(const elec_comp_t *comp)
bool libelec_sys_is_started(const elec_sys_t *sys)
void libelec_tie_set_all(elec_comp_t *comp, bool tied)
double libelec_cb_get_temp(const elec_comp_t *comp)
void libelec_batt_set_chg_rel(elec_comp_t *batt, double chg_rel)
double libelec_gen_set_random_volts(elec_comp_t *comp, double stddev)
bool libelec_chgr_get_working(const elec_comp_t *chgr)
size_t libelec_tie_get_list(elec_comp_t *comp, size_t cap, elec_comp_t *bus_list[])
elec_comp_t * libelec_comp_get_conn(const elec_comp_t *comp, size_t i)
void(* elec_user_cb_t)(elec_sys_t *sys, bool pre, void *userinfo)
double libelec_comp_get_out_volts(const elec_comp_t *comp)
double(* elec_get_rpm_cb_t)(elec_comp_t *comp, void *userinfo)
double libelec_comp_get_incap_volts(const elec_comp_t *comp)
void libelec_serialize(elec_sys_t *sys, conf_t *ser, const char *prefix)
size_t libelec_comp_get_num_conns(const elec_comp_t *comp)
void libelec_tie_set_v(elec_comp_t *comp, va_list ap)
elec_get_temp_cb_t libelec_batt_get_temp_cb(const elec_comp_t *batt)
void libelec_comp_set_failed(elec_comp_t *comp, bool failed)
void libelec_destroy(elec_sys_t *sys)
void libelec_gen_set_rpm(elec_comp_t *gen, double rpm)
bool libelec_sys_start(elec_sys_t *sys)
bool libelec_sys_can_start(const elec_sys_t *sys)
double libelec_sys_get_time_factor(const elec_sys_t *sys)
bool libelec_tie_get_all(elec_comp_t *comp)
void libelec_cb_set(elec_comp_t *comp, bool set)
void libelec_remove_user_cb(elec_sys_t *sys, bool pre, elec_user_cb_t cb, void *userinfo)
bool libelec_tie_get_v(elec_comp_t *tie, bool exhaustive, va_list ap)