ack.Acknowledgement acknowledgement = 1 {
  """Reply messages for actions that have no response data. Code `OK` means a positive acknowledgement, other codes
negative acknowledgements. A negative acknowledgement may be sent as the reply to any message, even if another reply
is expected. Negative acknowledgements may have a `message` describing the error condition."""

  AcknowledgementCode code = 1;
  string message = 2;

  <= ack.Acknowledgement(code, [message])
    """ack""";
}

version.VersionMessage version_message = 26 {
  """Request or reply for version information of the Hedgehog hardware, firmware, and server software."""

  bytes uc_id = 1;
  string hardware_version = 2;
  string firmware_version = 3;
  string server_version = 4;

  => version.Request()
    """version request => version reply""";
  <= version.Reply(uc_id, hardware_version, firmware_version, server_version)
    """version reply""";
}

emergency.EmergencyAction emergency_action = 27 {
  """Activates or releases the emergency-stop of the HWC."""

  bool activate = 1;

  => emergency.Action(activate)
    """emergency action => ack""";
}

emergency.EmergencyMessage emergency_message = 28 {
  """Request or reply for the current HWC emergency stop state.
  The command may change by an emergency action, or by a press of the emergency stop button on the HWC."""

  bool active = 1;
  nested Subscription subscription = 2;

  => emergency.Request()
    """emergency request => emergency command reply""";
  <= emergency.Reply(active)
    """emergency reply""";
  => emergency.Subscribe(subscription)
    """emergency subscribe => ack""";
  <- emergency.Update(active, subscription)
    """emergency update""";
}

io.IOAction io_action = 2 {
  """Changes the configuration of one IO port"""

  uint32 port = 1;
  IOFlags flags = 2;

  => io.Action(port, flags)
    """IO action => ack""";
}

io.IOCommandMessage io_command_message = 16 {
  """Request or reply for one IO port's current command. The command may change by an IO action."""

  uint32 port = 1;
  IOFlags flags = 2;
  nested Subscription subscription = 3;

  => io.CommandRequest(port)
    """IO command request => IO command reply""";
  <= io.CommandReply(port, flags)
    """IO command reply""";
  => io.CommandSubscribe(port, subscription)
    """IO command subscribe => ack""";
  <- io.CommandUpdate(port, flags, subscription)
    """IO command update""";
}

io.AnalogMessage analog_message = 3 {
  """Request or reply for one analog sensor's value"""

  uint32 port = 1;
  uint32 value = 2;
  nested Subscription subscription = 3;

  => analog.Request(port)
    """analog request => analog reply""";
  <= analog.Reply(port, value)
    """analog reply""";
  => analog.Subscribe(port, subscription)
    """analog subscribe => ack""";
  <- analog.Update(port, value, subscription)
    """analog update""";
}

io.DigitalMessage digital_message = 4 {
  """Request or reply for one digital sensor's value"""

  uint32 port = 1;
  bool value = 2;
  nested Subscription subscription = 3;

  => digital.Request(port)
    """digital request => digital reply""";
  <= digital.Reply(port, value)
    """digital reply""";
  => digital.Subscribe(port, subscription)
    """digital subscribe => ack""";
  <- digital.Update(port, value, subscription)
    """digital update""";
}

imu.ImuMessage imu_message = 9 {
  """Request or reply for Hedgehog's IMU.
  A request queries either the `RATE``, `ACCELERATION`, or `POSE` of the IMU,
  and the result contains data for all three axes."""

  ImuKind kind = 1;
  sint32 x = 2;
  sint32 y = 3;
  sint32 z = 4;
  nested Subscription subscription = 5;

  => imu.RateRequest()
    """IMU rate request => IMU rate reply""";
  <= imu.RateReply(x, y, z)
    """IMU rate reply""";
  => imu.RateSubscribe(subscription)
    """IMU rate subscribe => ack""";
  <- imu.RateUpdate(x, y, z, subscription)
    """IMU rate update""";

  => imu.AccelerationRequest()
    """IMU acceleration request => IMU acceleration reply""";
  <= imu.AccelerationReply(x, y, z)
    """IMU acceleration reply""";
  => imu.AccelerationSubscribe(subscription)
    """IMU acceleration subscribe => ack""";
  <- imu.AccelerationUpdate(x, y, z, subscription)
    """IMU acceleration update""";

  => imu.PoseRequest()
    """IMU pose request => IMU pose reply""";
  <= imu.PoseReply(x, y, z)
    """IMU pose reply""";
  => imu.PoseSubscribe(subscription)
    """IMU pose subscribe => ack""";
  <- imu.PoseUpdate(x, y, z, subscription)
    """IMU pose update""";
}

motor.MotorAction motor_action = 5 {
  """Command for one motor. By setting a relative or absolute goal position,
the motor will go into `reached_state` upon reaching the position."""

  uint32 port = 1;
  MotorState state = 2;
  sint32 amount = 3;
  MotorState reached_state = 4;
  oneof position {
    sint32 relative = 5;
    sint32 absolute = 6;
  }

  => motor.Action(port, state, amount, [reached_state, relative/absolute])
    """motor action => ack""";
}

motor.MotorConfigAction motor_config_action = 24 {
  """Configures one motor for DC, encoder, or stepper operation.
Stepper configuration requires two motor ports to be combined into one stepper
motor and thus only works for even motor ports (i.e. 0 and 2)."""

  uint32 port = 1;
  oneof config {
    nested Dummy dc = 2;
    nested EncoderConfig encoder = 3;
    nested Dummy stepper = 4;
  }

  => motor.ConfigAction(port, config)
    """motor config action => ack""";
}

motor.MotorCommandMessage motor_command_message = 17 {
  """Request or reply for one motor's current command.
The command may change by a motor action, or by reaching the goal position of a terminating motor action."""

  uint32 port = 1;
  oneof config {
    nested Dummy dc = 5;
    nested EncoderConfig encoder = 6;
    nested Dummy stepper = 7;
  }
  MotorState state = 2;
  sint32 amount = 3;
  nested Subscription subscription = 4;

  => motor.CommandRequest(port)
    """motor command request => motor command reply""";
  <= motor.CommandReply(port, config, state, amount)
    """motor command reply""";
  => motor.CommandSubscribe(port, subscription)
    """motor command subscribe => ack""";
  <- motor.CommandUpdate(port, config, state, amount, subscription)
    """motor command update""";
}

motor.MotorStateMessage motor_state_message = 6 {
  """Request or reply for one motor's state. The motor state may generally change at any time,
but returned values are approximations, and motor state requests may not be supported at all."""

  uint32 port = 1;
  sint32 velocity = 2;
  sint32 position = 3;
  nested Subscription subscription = 4;

  => motor.StateRequest(port)
    """motor state request => motor state reply""";
  <= motor.StateReply(port, velocity, position)
    """motor state reply""";
  => motor.StateSubscribe(port, subscription)
    """motor state subscribe => ack""";
  <- motor.StateUpdate(port, velocity, position, subscription)
    """motor state update""";
}

motor.MotorSetPositionAction motor_set_position_action = 18 {
  """Set one motor's position counter."""

  uint32 port = 1;
  sint32 position = 2;

  => motor.SetPositionAction(port, position)
    """set motor position action => ack""";
}

servo.ServoAction servo_action = 7 {
  """Set one servo target position"""

  uint32 port = 1;
  bool active = 2;
  uint32 position = 3;

  => servo.Action(port, active, position)
    """servo action => ack""";
}

servo.ServoCommandMessage servo_command_message = 19 {
  """Request or reply for one servo's current command. The command may change by a servo action,
actual servo movement is not reflected."""

  uint32 port = 1;
  bool active = 2;
  uint32 position = 3;
  nested Subscription subscription = 4;

  => servo.CommandRequest(port)
    """servo command request => servo command reply""";
  <= servo.CommandReply(port, active, position)
    """servo command reply""";
  => servo.CommandSubscribe(port, subscription)
    """servo command subscribe => ack""";
  <- servo.CommandUpdate(port, active, position, subscription)
    """servo command update""";
}

process.ProcessExecuteAction process_execute_action = 20 {
  """Invoke a process on the controller"""

  repeated string args = 2;
  string working_dir = 1;

  => process.ExecuteAction(*args, [working_dir])
    """process execute action => process execute reply""";
}

process.ProcessExecuteReply process_execute_reply = 21 {
  """Reply with the process' PID for communication purposes"""

  uint32 pid = 1;

  <= process.ExecuteReply(pid)
    """process execute reply""";
}

process.ProcessStreamMessage process_stream_message = 8 {
  """Send data to/receive data from a process' file streams.
The `STDIN` fileno may only be used for sending data to the process,
the others only for receiving data from the process.
An empty chunk means `EOF`."""

  uint32 pid = 1;
  ProcessFileno fileno = 2;
  bytes chunk = 3;

  => process.StreamAction(pid, fileno, chunk)
    """stream data action => ack""";
  <- process.StreamUpdate(pid, fileno, chunk)
    """stream data update""";
}

process.ProcessSignalAction process_signal_action = 22 {
  """Send signal to a process"""

  uint32 pid = 1;
  uint32 signal = 2;

  => process.SignalAction(pid, signal)
    """process signal action => ack""";
}

process.ProcessExitUpdate process_exit_update = 23 {
  """Signals that the process has exited.
A negative value `-N` indicates that the child was terminated by signal `N`."""

  uint32 pid = 1;
  int32 exit_code = 2;

  <- process.ExitUpdate(pid, exit_code)
    """process exit update""";
}

speaker.SpeakerAction speaker_action = 25 {
  """Sets the speaker frequency in Hertz; a frequency of zero turns the speaker off.
Only a range of frequencies may be valid depending on the Hardware."""

  uint32 frequency = 1;

  => speaker.Action(frequency)
    """servo action => ack""";
}
