Logo Search packages:      
Sourcecode: pcp-gui version File versions

timelord.cpp

/*
 * Copyright (c) 2007, Aconex.  All Rights Reserved.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */
#include "timelord.h"
#include <stdlib.h>

TimeClient::TimeClient(QTcpSocket *s, QObject *p) : QObject(p)
{
    my.ac = NULL;
    my.hc = NULL;
    my.socket = s;
    my.state = TimeClient::Disconnected;
    my.source = PmTime::NoSource;
    memset(&my.acktime, 0, sizeof(my.acktime));
    console->post(PmTime::DebugProtocol, "TimeClient initialised");
    connect(my.socket, SIGNAL(readyRead()), SLOT(readClient()));
    connect(my.socket, SIGNAL(disconnected()), SLOT(disconnectClient()));
}

TimeClient::~TimeClient()
{
    console->post(PmTime::DebugProtocol, "Destroying client %p", this);
    my.state = TimeClient::Disconnected;
    my.source = PmTime::NoSource;
    emit endConnect(this);
}

static char *stateString(int state)
{
    static char buffer[32];

    if (state == TimeClient::Disconnected)
      strcpy(buffer, "Disconnected State");
    else if (state == TimeClient::ClientConnectSET)
      strcpy(buffer, "ClientConnectSET State");
    else if (state == TimeClient::ServerConnectACK)
      strcpy(buffer, "ServerConnectACK State");
    else if (state == TimeClient::ServerNeedACK)
      strcpy(buffer, "ServerNeedACK State");
    else if (state == TimeClient::ClientReady)
      strcpy(buffer, "ClientReady State");
    else
      strcpy(buffer, "Unknown State");
    return buffer;
}

void TimeClient::disconnectClient()
{
    console->post(PmTime::DebugProtocol, "TimeClient::disconnectClient");
    delete this;
}

bool TimeClient::writeClient(PmTime::Packet *packet,
                        char *tz, int tzlen, char *label, int llen)
{
    if (packet->source != my.source)
      return true;

    switch (my.state) {
    case TimeClient::Disconnected:
      return true;
    case TimeClient::ClientConnectSET:
      if (packet->command == PmTime::ACK) {
          my.state = TimeClient::ServerConnectACK;
          break;
      }
      return true;
    case TimeClient::ServerConnectACK:
      break;
    case TimeClient::ClientReady:
      if (packet->command == PmTime::Step) {
          my.state = TimeClient::ServerNeedACK;
          my.acktime = packet->position;
      }
      else {
          my.state = TimeClient::ClientReady;
          memset(&my.acktime, 0, sizeof(my.acktime));
      }
      break;
    case TimeClient::ServerNeedACK:
      if (packet->command != PmTime::Step) {
          my.state = TimeClient::ClientReady;   // clear NEED_ACK
          memset(&my.acktime, 0, sizeof(my.acktime));
          break;
      }
      console->post(PmTime::DebugProtocol, "TimeClient::writeClient "
                  "SKIP STEP to pos=%u.%u when client %p in NEED_ACK",
                  packet->position.tv_sec,packet->position.tv_usec, this);
      return false;
    }

    int len = my.socket->write((const char *)packet, sizeof(PmTime::Packet));
    if (len != sizeof(PmTime::Packet)) {
      console->post(PmTime::DebugProtocol, "TimeCient::writeClient "
                  "wrote %d bytes not %d (%x command)",
                  len, packet->length, packet->command);
      my.state = TimeClient::Disconnected;
      endConnect(this);
    } else {
      console->post(PmTime::DebugProtocol, "TimeClient::writeClient "
                  "wrote %d bytes command=%x state=%u",
                  len, packet->command, my.state);
    }
    if (tzlen > 0 && len > 0 &&
      (len = my.socket->write(tz, tzlen)) != tzlen) {
      console->post(PmTime::DebugProtocol, "TimeClient::writeClient "
                  "wrote %d bytes not %d (timezone)", len, tzlen);
      my.state = TimeClient::Disconnected;
      endConnect(this);
    } else if (tzlen) {
      console->post(PmTime::DebugProtocol, "TimeClient::writeClient "
                  "wrote %d bytes of timezone successfully", len);
    }
    if (llen > 0 && len > 0 &&
      (len = my.socket->write(label, llen)) != llen) {
      console->post(PmTime::DebugProtocol, "TimeClient::writeClient "
                  "wrote %d bytes not %d (tz label)", len, llen);
      my.state = TimeClient::Disconnected;
      endConnect(this);
    } else if (llen) {
      console->post(PmTime::DebugProtocol, "TimeClient::writeClient "
                  "wrote %d bytes of tz label successfully", len);
    }
    return true;
}

void TimeClient::readClient(void)
{
    PmTime::Packet packet;
    char *payload = NULL;
    int bad = 0, len, sz;

    console->post(PmTime::DebugProtocol, "Reading data from client %p", this);

    len = my.socket->read((char *)&packet, sizeof(PmTime::Packet));
    if (len < 0) {
      console->post(PmTime::DebugProtocol, "Read error on client %p", this);
      bad = 1;
    } else if (packet.magic != PmTime::Magic) {
      console->post(PmTime::DebugProtocol, "Bad magic (%x) from client %p",
                  packet.magic, this);
      bad = 1;
    } else if (len != sizeof(PmTime::Packet)) {
      console->post(PmTime::DebugProtocol,
                  "Bad 1st read (want %d, got %d) on client %p",
                  len, sizeof(PmTime::Packet), this);
      bad = 1;
    } else if (packet.length > sizeof(PmTime::Packet)) {
      sz = packet.length - sizeof(PmTime::Packet);
      payload = (char *)malloc(sz);
      if (payload == NULL) {
          console->post(PmTime::DebugProtocol,
                        "No memory (%d) for second read on client %p",
                        sz, len, this);
          bad = 1;
      } else if ((len = my.socket->read(payload, sz)) != sz) {
          console->post(PmTime::DebugProtocol,
                        "Bad 2nd read (want %d, got %d) on client %p",
                        sz, len, this);
          bad = 1;
      }
      console->post(PmTime::DebugProtocol, "+%d message from client %p",
                        sz, this);
    } else {
      console->post(PmTime::DebugProtocol, "good message from client %p",
                        this);
    }

    if (!bad) {
      console->post(PmTime::DebugProtocol, "state %s message %d",
                        stateString(my.state), packet.command);
      switch(my.state) {
      case TimeClient::Disconnected:
          if (packet.command == PmTime::Set)
            console->post(PmTime::DebugProtocol,
                        "%s got new SET from client %p",
                        __func__, this);
          if (packet.source == PmTime::HostSource) {
            my.source = PmTime::HostSource;
            my.hc->setTime(&packet, payload);
          } else {
            my.source = PmTime::ArchiveSource;
            my.ac->setTime(&packet, payload);
          }
          my.state = TimeClient::ClientConnectSET;
          packet.command = PmTime::ACK;
          packet.length = sizeof(PmTime::Packet);
          writeClient(&packet);
          return;

      case TimeClient::ClientConnectSET:
          console->post(PmTime::DebugProtocol, "TimeClient::readClient "
                        "bad client %p command %d in ConnectSET state",
                        this, packet.command);
          break;

      case TimeClient::ServerConnectACK:
          if (packet.command == PmTime::ACK)
            my.state = TimeClient::ClientReady;
          break;

      case TimeClient::ServerNeedACK:
          if (packet.command != PmTime::ACK)
            break;
          if (packet.position.tv_sec == my.acktime.tv_sec &&
            packet.position.tv_usec == my.acktime.tv_usec) {
            console->post(PmTime::DebugProtocol, "TimeClient::readClient "
                        "good ACK client=%p (%u.%u)", this,
                        my.acktime.tv_sec, my.acktime.tv_usec);
            my.state = TimeClient::ClientReady;
            break;
          }
          console->post(PmTime::DebugProtocol, "TimeClient::readClient "
                        "BAD ACK client=%p (got %u.%u vs %u.%u)", this,
                        packet.position.tv_sec, packet.position.tv_usec,
                        my.acktime.tv_sec, my.acktime.tv_usec);
          bad = 1;
          break;

      case TimeClient::ClientReady:
          if (packet.command == PmTime::ACK) {
            console->post(PmTime::DebugProtocol, "TimeClient:: readClient "
                        "unexpected client %p ACK in Ready state", this);
          }
          break;
      }

      switch(packet.command) {
      case PmTime::GUIHide:
      case PmTime::GUIShow:
          console->post(PmTime::DebugProtocol, "TimeClient::readClient "
                        "HIDE/SHOW from client %p", this);
          if (my.source == PmTime::HostSource)
            my.hc->popup(packet.command == PmTime::GUIShow);
          if (my.source == PmTime::ArchiveSource)
            my.ac->popup(packet.command == PmTime::GUIShow);
          break;
      case PmTime::Bounds:
          console->post(PmTime::DebugProtocol, "TimeClient::readClient "
                        "BOUNDS from client %p", this);
          my.ac->addBound(&packet, payload);
          break;
      case PmTime::ACK:
          break;
      default:
          console->post(PmTime::DebugProtocol, "TimeClient::readClient "
                        "unknown command %d from client %p",
                        packet.command, this);
          bad = 1;
      }
    }

    if (bad)
      reset();
}

void TimeClient::reset()
{
    console->post(PmTime::DebugProtocol, "TimeClient::reset");
#if 0
    if (my.source == PmTime::HostSource)
      my.hc->stop();
    else
      my.ac->stop();
#endif
}

TimeLord::TimeLord(QApplication *app)
{
    my.ac = NULL;
    my.hc = NULL;
    connect(this, SIGNAL(lastClientExit()), app, SLOT(quit()));
    connect(this, SIGNAL(lastClientExit()), this, SLOT(quit()));
    connect(this, SIGNAL(newConnection()), SLOT(newConnection()));
    console->post(PmTime::DebugProtocol, "TimeLord initialised");
}

void TimeLord::quit()
{
    if (my.ac)
      my.ac->quit();
    if (my.hc)
      my.hc->quit();
}

void TimeLord::setContext(PmTimeLive *live, PmTimeArch *arch)
{
    my.hc = live;
    connect(live, SIGNAL(timePulse(PmTime::Packet *)),
                SLOT(timePulse(PmTime::Packet *)));
    connect(live, SIGNAL(vcrModePulse(PmTime::Packet *, int)),
                SLOT(vcrModePulse(PmTime::Packet *, int)));
    connect(live, SIGNAL(tzPulse(PmTime::Packet *, char *, int, char *, int)),
                SLOT(tzPulse(PmTime::Packet *, char *, int, char *, int)));
    my.ac = arch;
    connect(arch, SIGNAL(timePulse(PmTime::Packet *)),
                SLOT(timePulse(PmTime::Packet *)));
    connect(arch, SIGNAL(boundsPulse(PmTime::Packet *)),
                SLOT(boundsPulse(PmTime::Packet *)));
    connect(arch, SIGNAL(vcrModePulse(PmTime::Packet *, int)),
                SLOT(vcrModePulse(PmTime::Packet *, int)));
    connect(arch, SIGNAL(tzPulse(PmTime::Packet *, char *, int, char *, int)),
                SLOT(tzPulse(PmTime::Packet *, char *, int, char *, int)));
}

void TimeLord::newConnection(void)
{
    TimeClient *c = new TimeClient(nextPendingConnection(), this);

    console->post(PmTime::DebugProtocol, "Adding new client %p", c);
    c->setContext(my.ac, my.hc);
    connect(c, SIGNAL(endConnect(TimeClient *)),
             SLOT(endConnect(TimeClient *)));
    my.clientlist.append(c);
}

void TimeLord::endConnect(TimeClient *client)
{
    console->post(PmTime::DebugProtocol, "Removing client %p", client);
    my.clientlist.removeAll(client);
    if (my.clientlist.isEmpty()) {
      console->post(PmTime::DebugProtocol, "No clients remain, exiting");
      emit lastClientExit();
    }
}

void TimeLord::timePulse(PmTime::Packet *packet)
{
    QList<TimeClient*> overrunClients;

#if DESPERATE
    static int sequence;
    int localSequence = sequence++;
    console->post(PmTime::DebugProtocol, "TimeLord::timePulse %d (%d clients)",
                               localSequence, my.clientlist.count());
#endif

    packet->magic = PmTime::Magic;
    packet->length = sizeof(PmTime::Packet);
    packet->command = PmTime::Step;
    for (int i = 0; i < my.clientlist.size(); i++)
      if (my.clientlist.at(i)->writeClient(packet) == false)
          overrunClients.append(my.clientlist.at(i));
    for (int i = 0; i < overrunClients.size(); i++)
      overrunClients.at(i)->reset();

#if DESPERATE
    console->post(PmTime::DebugProtocol, "TimeLord::timePulse ended %d (%d)",
                               localSequence, overrunClients.size());
#endif
}

void TimeLord::boundsPulse(PmTime::Packet *packet)
{
    console->post(PmTime::DebugProtocol, "TimeLord::boundsPulse (%d clients)",
                               my.clientlist.count());
    packet->magic = PmTime::Magic;
    packet->length = sizeof(PmTime::Packet);
    packet->command = PmTime::Bounds;
    for (int i = 0; i < my.clientlist.size(); i++)
      my.clientlist.at(i)->writeClient(packet);
}

void TimeLord::vcrModePulse(PmTime::Packet *packet, int drag)
{
    console->post(PmTime::DebugProtocol, "TimeLord::vcrModePulse (%d clients)"
            " %d", my.clientlist.count(), drag);
    packet->magic = PmTime::Magic;
    packet->length = sizeof(PmTime::Packet);
    packet->command = drag ? PmTime::VCRModeDrag : PmTime::VCRMode;
    for (int i = 0; i < my.clientlist.size(); i++)
      my.clientlist.at(i)->writeClient(packet);
}

void TimeLord::tzPulse(PmTime::Packet *packet,
                        char *tz, int tzlen, char *l, int llen)
{
    console->post(PmTime::DebugProtocol, "TimeLord::tzPulse (%d clients)"
            " - %s/%d/%d", my.clientlist.count(), tz, tzlen, llen);
    packet->magic = PmTime::Magic;
    packet->length = sizeof(PmTime::Packet) + tzlen + llen;
    packet->command = PmTime::TZ;
    for (int i = 0; i < my.clientlist.size(); i++)
      my.clientlist.at(i)->writeClient(packet, tz, tzlen, l, llen);
}

Generated by  Doxygen 1.6.0   Back to index